ia64/linux-2.6.18-xen.hg

annotate drivers/ieee1394/iso.c @ 0:831230e53067

Import 2.6.18 from kernel.org tarball.
author Ian Campbell <ian.campbell@xensource.com>
date Wed Apr 11 14:15:44 2007 +0100 (2007-04-11)
parents
children
rev   line source
ian@0 1 /*
ian@0 2 * IEEE 1394 for Linux
ian@0 3 *
ian@0 4 * kernel ISO transmission/reception
ian@0 5 *
ian@0 6 * Copyright (C) 2002 Maas Digital LLC
ian@0 7 *
ian@0 8 * This code is licensed under the GPL. See the file COPYING in the root
ian@0 9 * directory of the kernel sources for details.
ian@0 10 */
ian@0 11
ian@0 12 #include <linux/slab.h>
ian@0 13 #include <linux/sched.h>
ian@0 14 #include "iso.h"
ian@0 15
ian@0 16 void hpsb_iso_stop(struct hpsb_iso *iso)
ian@0 17 {
ian@0 18 if (!(iso->flags & HPSB_ISO_DRIVER_STARTED))
ian@0 19 return;
ian@0 20
ian@0 21 iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ?
ian@0 22 XMIT_STOP : RECV_STOP, 0);
ian@0 23 iso->flags &= ~HPSB_ISO_DRIVER_STARTED;
ian@0 24 }
ian@0 25
ian@0 26 void hpsb_iso_shutdown(struct hpsb_iso *iso)
ian@0 27 {
ian@0 28 if (iso->flags & HPSB_ISO_DRIVER_INIT) {
ian@0 29 hpsb_iso_stop(iso);
ian@0 30 iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ?
ian@0 31 XMIT_SHUTDOWN : RECV_SHUTDOWN, 0);
ian@0 32 iso->flags &= ~HPSB_ISO_DRIVER_INIT;
ian@0 33 }
ian@0 34
ian@0 35 dma_region_free(&iso->data_buf);
ian@0 36 kfree(iso);
ian@0 37 }
ian@0 38
ian@0 39 static struct hpsb_iso *hpsb_iso_common_init(struct hpsb_host *host,
ian@0 40 enum hpsb_iso_type type,
ian@0 41 unsigned int data_buf_size,
ian@0 42 unsigned int buf_packets,
ian@0 43 int channel, int dma_mode,
ian@0 44 int irq_interval,
ian@0 45 void (*callback) (struct hpsb_iso
ian@0 46 *))
ian@0 47 {
ian@0 48 struct hpsb_iso *iso;
ian@0 49 int dma_direction;
ian@0 50
ian@0 51 /* make sure driver supports the ISO API */
ian@0 52 if (!host->driver->isoctl) {
ian@0 53 printk(KERN_INFO
ian@0 54 "ieee1394: host driver '%s' does not support the rawiso API\n",
ian@0 55 host->driver->name);
ian@0 56 return NULL;
ian@0 57 }
ian@0 58
ian@0 59 /* sanitize parameters */
ian@0 60
ian@0 61 if (buf_packets < 2)
ian@0 62 buf_packets = 2;
ian@0 63
ian@0 64 if ((dma_mode < HPSB_ISO_DMA_DEFAULT)
ian@0 65 || (dma_mode > HPSB_ISO_DMA_PACKET_PER_BUFFER))
ian@0 66 dma_mode = HPSB_ISO_DMA_DEFAULT;
ian@0 67
ian@0 68 if ((irq_interval < 0) || (irq_interval > buf_packets / 4))
ian@0 69 irq_interval = buf_packets / 4;
ian@0 70 if (irq_interval == 0) /* really interrupt for each packet */
ian@0 71 irq_interval = 1;
ian@0 72
ian@0 73 if (channel < -1 || channel >= 64)
ian@0 74 return NULL;
ian@0 75
ian@0 76 /* channel = -1 is OK for multi-channel recv but not for xmit */
ian@0 77 if (type == HPSB_ISO_XMIT && channel < 0)
ian@0 78 return NULL;
ian@0 79
ian@0 80 /* allocate and write the struct hpsb_iso */
ian@0 81
ian@0 82 iso =
ian@0 83 kmalloc(sizeof(*iso) +
ian@0 84 buf_packets * sizeof(struct hpsb_iso_packet_info),
ian@0 85 GFP_KERNEL);
ian@0 86 if (!iso)
ian@0 87 return NULL;
ian@0 88
ian@0 89 iso->infos = (struct hpsb_iso_packet_info *)(iso + 1);
ian@0 90
ian@0 91 iso->type = type;
ian@0 92 iso->host = host;
ian@0 93 iso->hostdata = NULL;
ian@0 94 iso->callback = callback;
ian@0 95 init_waitqueue_head(&iso->waitq);
ian@0 96 iso->channel = channel;
ian@0 97 iso->irq_interval = irq_interval;
ian@0 98 iso->dma_mode = dma_mode;
ian@0 99 dma_region_init(&iso->data_buf);
ian@0 100 iso->buf_size = PAGE_ALIGN(data_buf_size);
ian@0 101 iso->buf_packets = buf_packets;
ian@0 102 iso->pkt_dma = 0;
ian@0 103 iso->first_packet = 0;
ian@0 104 spin_lock_init(&iso->lock);
ian@0 105
ian@0 106 if (iso->type == HPSB_ISO_XMIT) {
ian@0 107 iso->n_ready_packets = iso->buf_packets;
ian@0 108 dma_direction = PCI_DMA_TODEVICE;
ian@0 109 } else {
ian@0 110 iso->n_ready_packets = 0;
ian@0 111 dma_direction = PCI_DMA_FROMDEVICE;
ian@0 112 }
ian@0 113
ian@0 114 atomic_set(&iso->overflows, 0);
ian@0 115 iso->bytes_discarded = 0;
ian@0 116 iso->flags = 0;
ian@0 117 iso->prebuffer = 0;
ian@0 118
ian@0 119 /* allocate the packet buffer */
ian@0 120 if (dma_region_alloc
ian@0 121 (&iso->data_buf, iso->buf_size, host->pdev, dma_direction))
ian@0 122 goto err;
ian@0 123
ian@0 124 return iso;
ian@0 125
ian@0 126 err:
ian@0 127 hpsb_iso_shutdown(iso);
ian@0 128 return NULL;
ian@0 129 }
ian@0 130
ian@0 131 int hpsb_iso_n_ready(struct hpsb_iso *iso)
ian@0 132 {
ian@0 133 unsigned long flags;
ian@0 134 int val;
ian@0 135
ian@0 136 spin_lock_irqsave(&iso->lock, flags);
ian@0 137 val = iso->n_ready_packets;
ian@0 138 spin_unlock_irqrestore(&iso->lock, flags);
ian@0 139
ian@0 140 return val;
ian@0 141 }
ian@0 142
ian@0 143 struct hpsb_iso *hpsb_iso_xmit_init(struct hpsb_host *host,
ian@0 144 unsigned int data_buf_size,
ian@0 145 unsigned int buf_packets,
ian@0 146 int channel,
ian@0 147 int speed,
ian@0 148 int irq_interval,
ian@0 149 void (*callback) (struct hpsb_iso *))
ian@0 150 {
ian@0 151 struct hpsb_iso *iso = hpsb_iso_common_init(host, HPSB_ISO_XMIT,
ian@0 152 data_buf_size, buf_packets,
ian@0 153 channel,
ian@0 154 HPSB_ISO_DMA_DEFAULT,
ian@0 155 irq_interval, callback);
ian@0 156 if (!iso)
ian@0 157 return NULL;
ian@0 158
ian@0 159 iso->speed = speed;
ian@0 160
ian@0 161 /* tell the driver to start working */
ian@0 162 if (host->driver->isoctl(iso, XMIT_INIT, 0))
ian@0 163 goto err;
ian@0 164
ian@0 165 iso->flags |= HPSB_ISO_DRIVER_INIT;
ian@0 166 return iso;
ian@0 167
ian@0 168 err:
ian@0 169 hpsb_iso_shutdown(iso);
ian@0 170 return NULL;
ian@0 171 }
ian@0 172
ian@0 173 struct hpsb_iso *hpsb_iso_recv_init(struct hpsb_host *host,
ian@0 174 unsigned int data_buf_size,
ian@0 175 unsigned int buf_packets,
ian@0 176 int channel,
ian@0 177 int dma_mode,
ian@0 178 int irq_interval,
ian@0 179 void (*callback) (struct hpsb_iso *))
ian@0 180 {
ian@0 181 struct hpsb_iso *iso = hpsb_iso_common_init(host, HPSB_ISO_RECV,
ian@0 182 data_buf_size, buf_packets,
ian@0 183 channel, dma_mode,
ian@0 184 irq_interval, callback);
ian@0 185 if (!iso)
ian@0 186 return NULL;
ian@0 187
ian@0 188 /* tell the driver to start working */
ian@0 189 if (host->driver->isoctl(iso, RECV_INIT, 0))
ian@0 190 goto err;
ian@0 191
ian@0 192 iso->flags |= HPSB_ISO_DRIVER_INIT;
ian@0 193 return iso;
ian@0 194
ian@0 195 err:
ian@0 196 hpsb_iso_shutdown(iso);
ian@0 197 return NULL;
ian@0 198 }
ian@0 199
ian@0 200 int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel)
ian@0 201 {
ian@0 202 if (iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64)
ian@0 203 return -EINVAL;
ian@0 204 return iso->host->driver->isoctl(iso, RECV_LISTEN_CHANNEL, channel);
ian@0 205 }
ian@0 206
ian@0 207 int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel)
ian@0 208 {
ian@0 209 if (iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64)
ian@0 210 return -EINVAL;
ian@0 211 return iso->host->driver->isoctl(iso, RECV_UNLISTEN_CHANNEL, channel);
ian@0 212 }
ian@0 213
ian@0 214 int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask)
ian@0 215 {
ian@0 216 if (iso->type != HPSB_ISO_RECV || iso->channel != -1)
ian@0 217 return -EINVAL;
ian@0 218 return iso->host->driver->isoctl(iso, RECV_SET_CHANNEL_MASK,
ian@0 219 (unsigned long)&mask);
ian@0 220 }
ian@0 221
ian@0 222 int hpsb_iso_recv_flush(struct hpsb_iso *iso)
ian@0 223 {
ian@0 224 if (iso->type != HPSB_ISO_RECV)
ian@0 225 return -EINVAL;
ian@0 226 return iso->host->driver->isoctl(iso, RECV_FLUSH, 0);
ian@0 227 }
ian@0 228
ian@0 229 static int do_iso_xmit_start(struct hpsb_iso *iso, int cycle)
ian@0 230 {
ian@0 231 int retval = iso->host->driver->isoctl(iso, XMIT_START, cycle);
ian@0 232 if (retval)
ian@0 233 return retval;
ian@0 234
ian@0 235 iso->flags |= HPSB_ISO_DRIVER_STARTED;
ian@0 236 return retval;
ian@0 237 }
ian@0 238
ian@0 239 int hpsb_iso_xmit_start(struct hpsb_iso *iso, int cycle, int prebuffer)
ian@0 240 {
ian@0 241 if (iso->type != HPSB_ISO_XMIT)
ian@0 242 return -1;
ian@0 243
ian@0 244 if (iso->flags & HPSB_ISO_DRIVER_STARTED)
ian@0 245 return 0;
ian@0 246
ian@0 247 if (cycle < -1)
ian@0 248 cycle = -1;
ian@0 249 else if (cycle >= 8000)
ian@0 250 cycle %= 8000;
ian@0 251
ian@0 252 iso->xmit_cycle = cycle;
ian@0 253
ian@0 254 if (prebuffer < 0)
ian@0 255 prebuffer = iso->buf_packets - 1;
ian@0 256 else if (prebuffer == 0)
ian@0 257 prebuffer = 1;
ian@0 258
ian@0 259 if (prebuffer >= iso->buf_packets)
ian@0 260 prebuffer = iso->buf_packets - 1;
ian@0 261
ian@0 262 iso->prebuffer = prebuffer;
ian@0 263
ian@0 264 /* remember the starting cycle; DMA will commence from xmit_queue_packets()
ian@0 265 once enough packets have been buffered */
ian@0 266 iso->start_cycle = cycle;
ian@0 267
ian@0 268 return 0;
ian@0 269 }
ian@0 270
ian@0 271 int hpsb_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync)
ian@0 272 {
ian@0 273 int retval = 0;
ian@0 274 int isoctl_args[3];
ian@0 275
ian@0 276 if (iso->type != HPSB_ISO_RECV)
ian@0 277 return -1;
ian@0 278
ian@0 279 if (iso->flags & HPSB_ISO_DRIVER_STARTED)
ian@0 280 return 0;
ian@0 281
ian@0 282 if (cycle < -1)
ian@0 283 cycle = -1;
ian@0 284 else if (cycle >= 8000)
ian@0 285 cycle %= 8000;
ian@0 286
ian@0 287 isoctl_args[0] = cycle;
ian@0 288
ian@0 289 if (tag_mask < 0)
ian@0 290 /* match all tags */
ian@0 291 tag_mask = 0xF;
ian@0 292 isoctl_args[1] = tag_mask;
ian@0 293
ian@0 294 isoctl_args[2] = sync;
ian@0 295
ian@0 296 retval =
ian@0 297 iso->host->driver->isoctl(iso, RECV_START,
ian@0 298 (unsigned long)&isoctl_args[0]);
ian@0 299 if (retval)
ian@0 300 return retval;
ian@0 301
ian@0 302 iso->flags |= HPSB_ISO_DRIVER_STARTED;
ian@0 303 return retval;
ian@0 304 }
ian@0 305
ian@0 306 /* check to make sure the user has not supplied bogus values of offset/len
ian@0 307 that would cause the kernel to access memory outside the buffer */
ian@0 308
ian@0 309 static int hpsb_iso_check_offset_len(struct hpsb_iso *iso,
ian@0 310 unsigned int offset, unsigned short len,
ian@0 311 unsigned int *out_offset,
ian@0 312 unsigned short *out_len)
ian@0 313 {
ian@0 314 if (offset >= iso->buf_size)
ian@0 315 return -EFAULT;
ian@0 316
ian@0 317 /* make sure the packet does not go beyond the end of the buffer */
ian@0 318 if (offset + len > iso->buf_size)
ian@0 319 return -EFAULT;
ian@0 320
ian@0 321 /* check for wrap-around */
ian@0 322 if (offset + len < offset)
ian@0 323 return -EFAULT;
ian@0 324
ian@0 325 /* now we can trust 'offset' and 'length' */
ian@0 326 *out_offset = offset;
ian@0 327 *out_len = len;
ian@0 328
ian@0 329 return 0;
ian@0 330 }
ian@0 331
ian@0 332 int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len,
ian@0 333 u8 tag, u8 sy)
ian@0 334 {
ian@0 335 struct hpsb_iso_packet_info *info;
ian@0 336 unsigned long flags;
ian@0 337 int rv;
ian@0 338
ian@0 339 if (iso->type != HPSB_ISO_XMIT)
ian@0 340 return -EINVAL;
ian@0 341
ian@0 342 /* is there space in the buffer? */
ian@0 343 if (iso->n_ready_packets <= 0) {
ian@0 344 return -EBUSY;
ian@0 345 }
ian@0 346
ian@0 347 info = &iso->infos[iso->first_packet];
ian@0 348
ian@0 349 /* check for bogus offset/length */
ian@0 350 if (hpsb_iso_check_offset_len
ian@0 351 (iso, offset, len, &info->offset, &info->len))
ian@0 352 return -EFAULT;
ian@0 353
ian@0 354 info->tag = tag;
ian@0 355 info->sy = sy;
ian@0 356
ian@0 357 spin_lock_irqsave(&iso->lock, flags);
ian@0 358
ian@0 359 rv = iso->host->driver->isoctl(iso, XMIT_QUEUE, (unsigned long)info);
ian@0 360 if (rv)
ian@0 361 goto out;
ian@0 362
ian@0 363 /* increment cursors */
ian@0 364 iso->first_packet = (iso->first_packet + 1) % iso->buf_packets;
ian@0 365 iso->xmit_cycle = (iso->xmit_cycle + 1) % 8000;
ian@0 366 iso->n_ready_packets--;
ian@0 367
ian@0 368 if (iso->prebuffer != 0) {
ian@0 369 iso->prebuffer--;
ian@0 370 if (iso->prebuffer <= 0) {
ian@0 371 iso->prebuffer = 0;
ian@0 372 rv = do_iso_xmit_start(iso, iso->start_cycle);
ian@0 373 }
ian@0 374 }
ian@0 375
ian@0 376 out:
ian@0 377 spin_unlock_irqrestore(&iso->lock, flags);
ian@0 378 return rv;
ian@0 379 }
ian@0 380
ian@0 381 int hpsb_iso_xmit_sync(struct hpsb_iso *iso)
ian@0 382 {
ian@0 383 if (iso->type != HPSB_ISO_XMIT)
ian@0 384 return -EINVAL;
ian@0 385
ian@0 386 return wait_event_interruptible(iso->waitq,
ian@0 387 hpsb_iso_n_ready(iso) ==
ian@0 388 iso->buf_packets);
ian@0 389 }
ian@0 390
ian@0 391 void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error)
ian@0 392 {
ian@0 393 unsigned long flags;
ian@0 394 spin_lock_irqsave(&iso->lock, flags);
ian@0 395
ian@0 396 /* predict the cycle of the next packet to be queued */
ian@0 397
ian@0 398 /* jump ahead by the number of packets that are already buffered */
ian@0 399 cycle += iso->buf_packets - iso->n_ready_packets;
ian@0 400 cycle %= 8000;
ian@0 401
ian@0 402 iso->xmit_cycle = cycle;
ian@0 403 iso->n_ready_packets++;
ian@0 404 iso->pkt_dma = (iso->pkt_dma + 1) % iso->buf_packets;
ian@0 405
ian@0 406 if (iso->n_ready_packets == iso->buf_packets || error != 0) {
ian@0 407 /* the buffer has run empty! */
ian@0 408 atomic_inc(&iso->overflows);
ian@0 409 }
ian@0 410
ian@0 411 spin_unlock_irqrestore(&iso->lock, flags);
ian@0 412 }
ian@0 413
ian@0 414 void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len,
ian@0 415 u16 total_len, u16 cycle, u8 channel, u8 tag,
ian@0 416 u8 sy)
ian@0 417 {
ian@0 418 unsigned long flags;
ian@0 419 spin_lock_irqsave(&iso->lock, flags);
ian@0 420
ian@0 421 if (iso->n_ready_packets == iso->buf_packets) {
ian@0 422 /* overflow! */
ian@0 423 atomic_inc(&iso->overflows);
ian@0 424 /* Record size of this discarded packet */
ian@0 425 iso->bytes_discarded += total_len;
ian@0 426 } else {
ian@0 427 struct hpsb_iso_packet_info *info = &iso->infos[iso->pkt_dma];
ian@0 428 info->offset = offset;
ian@0 429 info->len = len;
ian@0 430 info->total_len = total_len;
ian@0 431 info->cycle = cycle;
ian@0 432 info->channel = channel;
ian@0 433 info->tag = tag;
ian@0 434 info->sy = sy;
ian@0 435
ian@0 436 iso->pkt_dma = (iso->pkt_dma + 1) % iso->buf_packets;
ian@0 437 iso->n_ready_packets++;
ian@0 438 }
ian@0 439
ian@0 440 spin_unlock_irqrestore(&iso->lock, flags);
ian@0 441 }
ian@0 442
ian@0 443 int hpsb_iso_recv_release_packets(struct hpsb_iso *iso, unsigned int n_packets)
ian@0 444 {
ian@0 445 unsigned long flags;
ian@0 446 unsigned int i;
ian@0 447 int rv = 0;
ian@0 448
ian@0 449 if (iso->type != HPSB_ISO_RECV)
ian@0 450 return -1;
ian@0 451
ian@0 452 spin_lock_irqsave(&iso->lock, flags);
ian@0 453 for (i = 0; i < n_packets; i++) {
ian@0 454 rv = iso->host->driver->isoctl(iso, RECV_RELEASE,
ian@0 455 (unsigned long)&iso->infos[iso->
ian@0 456 first_packet]);
ian@0 457 if (rv)
ian@0 458 break;
ian@0 459
ian@0 460 iso->first_packet = (iso->first_packet + 1) % iso->buf_packets;
ian@0 461 iso->n_ready_packets--;
ian@0 462
ian@0 463 /* release memory from packets discarded when queue was full */
ian@0 464 if (iso->n_ready_packets == 0) { /* Release only after all prior packets handled */
ian@0 465 if (iso->bytes_discarded != 0) {
ian@0 466 struct hpsb_iso_packet_info inf;
ian@0 467 inf.total_len = iso->bytes_discarded;
ian@0 468 iso->host->driver->isoctl(iso, RECV_RELEASE,
ian@0 469 (unsigned long)&inf);
ian@0 470 iso->bytes_discarded = 0;
ian@0 471 }
ian@0 472 }
ian@0 473 }
ian@0 474 spin_unlock_irqrestore(&iso->lock, flags);
ian@0 475 return rv;
ian@0 476 }
ian@0 477
ian@0 478 void hpsb_iso_wake(struct hpsb_iso *iso)
ian@0 479 {
ian@0 480 wake_up_interruptible(&iso->waitq);
ian@0 481
ian@0 482 if (iso->callback)
ian@0 483 iso->callback(iso);
ian@0 484 }