Esempio n. 1
0
int dvblo_char_exit(void)
{
	int rv = SUCCESS, i, j;
	switch(initlev) {
		case 3:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
			class_destroy(dvblo_class);
#else
			class_simple_destroy(dvblo_class);
#endif
		case 2:
			/* NOTE: we have to lock dvblo_char_chardevs_sem to avoid that after
			 * releasing all devices a new device is created again, becaus our char 
			 * device driver is still registered.
			 */
			down(&dvblo_char_chardevs_sem);
			for(j=0; j<DVBLO_CHAR_DEVMAX; j++) {
				if(chardevs[j].used != 0) {
					// Wait, until all open file handles have been closed
					int waitc = 0;
					while(chardevs[j].hcount > 0) {
						mprintk(KERN_INFO, "There are %i open file handles for device %s. %s...\n", 
							chardevs[j].hcount, chardevs[j].name, waitc > 0 ? "Still waiting" : "Waiting");
						up(&dvblo_char_chardevs_sem);
						wait_event(dvblo_char_chardevs_wq, (chardevs[j].hcount == 0));
						down(&dvblo_char_chardevs_sem);
						waitc++;
					}
					// now that all file handles are closed, release the device
					i = dvblo_chardev_release(&chardevs[j]);
					if(i != SUCCESS)
					{
						mprintk(KERN_ALERT, "failed to release char device %s: %d\n", chardevs[j].name, i);
						if(rv == 0)
							rv = i;
					}
				}
			}
#if defined(USE_CDEV) && USE_CDEV != 0
			cdev_del(&dvblo_char_cdev);
#else
			i = unregister_chrdev(dvblo_char_major, DVBLO_NAME);
			if (i < 0) {
				mprintk(KERN_ALERT, "failed to unregister char device: %d\n", i);
				if(rv == 0)
					rv = i;
			}
#endif
			up(&dvblo_char_chardevs_sem);
		case 1:
#if defined(USE_CDEV) && USE_CDEV != 0
			unregister_chrdev_region(MKDEV(dvblo_char_major, 0), DVBLO_CHAR_DEVMAX);
#endif
			dvblo_char_major = 0;
		default:
			break;
	}
	initlev = 0;
	return rv;
}
Esempio n. 2
0
static ssize_t clsdev_op_store(int what, struct class_device *dev, const char *buf, size_t count)
{
	char sbuf[64];
	ssize_t rv = SUCCESS, minor = MINOR(dev->devt);
	dprintk(DBGLEV_CHAR3, "what=%d, dev=%p, buf=%p, count=%u\n", what, dev, buf, count);
	if(minor >= DVBLO_CHAR_DEVMAX || chardevs[minor].used == 0)
		rv = -ENODEV;
	else if(down_interruptible(&dvblo_char_chardevs_sem) != 0)
		rv = -ERESTARTSYS;
	else {
		strncat(sbuf, buf, min(sizeof(sbuf)-1, count));
		if(chardevs[minor].used == 0)
			rv = -ENODEV;
		else switch(what) {
			case 1: /* Set the L2-framing mode */
			{
				char *end = NULL;
				unsigned long val = simple_strtoul(sbuf, &end, 0);
				if(end == NULL || (end[0] != '\0' && !isspace(end[0])))
					rv = -EINVAL;
				else if(val != 188) {
					mprintk(KERN_ERR, "Invalid value for TS packet size: the supplied value (%lu) was wrong: must be 188\n", val);
					rv = -EINVAL;
				} else {
					chardevs[minor].cfg.ts_sz = val;
					rv = count;
				}
				break;
			}
			case 2: /* Set the (default) per-handle write buffer size */
			{
				char *end = NULL;
				unsigned long val = simple_strtoul(sbuf, &end, 0);
				if(end == NULL || (end[0] != '\0' && !isspace(end[0])))
					rv = -EINVAL;
				else if((val % chardevs[minor].cfg.ts_sz) != 0) {
					mprintk(KERN_ERR, "Invalid value for per-handle write buffer size: the supplied value (%lu) was wrong: must be a multiple of %u\n", val, chardevs[minor].cfg.ts_sz);
					rv = -EINVAL;
				} else {
					chardevs[minor].cfg.hwrbuf_sz = val;
					rv = count;
				}
				break;
			}
			default:
				rv = -EINVAL;
				break;
		}
		up(&dvblo_char_chardevs_sem);
	}
	return rv;
}
Esempio n. 3
0
static int dvblo_chardev_release(struct dvblo_chardev *chardev)
{
	int rv = SUCCESS, i;
	if(chardev->used == 0) {
		rv = -EINVAL;
	} else if(chardev->hcount > 0) {
		rv = -EAGAIN;
	} else {
		if(chardev->initdone != 0)
			dprintk(0, "releasing char device with minor device number %u\n", chardev->minor);
		switch(chardev->initlev)
		{
			case 3:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
			for(i=0; i<(sizeof(clsdev_attrs)/sizeof(clsdev_attrs[0])) && rv == SUCCESS; i++)
				class_device_remove_file(chardev->clsdev, &clsdev_attrs[i]);
#endif
			case 2:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
				class_device_destroy(dvblo_class, MKDEV(dvblo_char_major, chardev->minor));
#else
				/* The old-style "simple" class API */
				class_simple_device_remove(MKDEV(dvblo_char_major, chardev->minor));
#endif
			case 1:
				i = dvblo_adap_destroy(chardev->dvblo);
				if(i < 0) {
					mprintk(KERN_ALERT, "failed to destroy virtual DVB adapter: %d\n", i);
					if(rv == 0)
						rv = i;
				}
			default:
				break;
		}
		if(chardev->initdone != 0)		
			mprintk(KERN_INFO, "removed character device %s\n", chardev->name);
		chardev->initlev = 0;
		chardev->initdone = 0;
		chardev->used = 0;
	}
	return rv;
}
Esempio n. 4
0
/* wait until all locks are released */
void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line)
{
	int max_count = 5 * HZ;

	if (atomic_read(lockp) < 0) {
		mprintk(KERN_WARNING "seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line); // MJR
		return;
	}
	while (atomic_read(lockp) > 0) {
		if (max_count == 0) {
			snd_mprintk(KERN_WARNING "seq_lock: timeout [%d left] in %s:%d\n", atomic_read(lockp), file, line); // MJR
			break;
		}
		schedule_timeout_uninterruptible(1);
		max_count--;
	}
}
Esempio n. 5
0
int snd_opl3_init(struct snd_opl3 *opl3)
{
	if (! opl3->command) {
		mprintk(KERN_ERR "snd_opl3_init: command not defined!\n"); // MJR
		return -EINVAL;
	}

	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TEST, OPL3_ENABLE_WAVE_SELECT);
	/* Melodic mode */
	opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 0x00);

	switch (opl3->hardware & OPL3_HW_MASK) {
	case OPL3_HW_OPL2:
		opl3->max_voices = MAX_OPL2_VOICES;
		break;
	case OPL3_HW_OPL3:
	case OPL3_HW_OPL4:
		opl3->max_voices = MAX_OPL3_VOICES;
		/* Enter OPL3 mode */
		opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE);
	}
	return 0;
}
Esempio n. 6
0
int dvblo_char_init(void)
{
	int rv = SUCCESS, i;
	do
	{
		if(initlev > 0) {
			rv = -EINVAL;
			break;
		}
		/* initialize array of char devices */
		for(i=0; i<DVBLO_CHAR_DEVMAX; i++) {
			chardevs[i].used = 0;
			chardevs[i].minor = i;
		}
#if defined(USE_CDEV) && USE_CDEV != 0
		if(dvblo_char_major > 0) {
			/* we explicitly request the major device number dvblo_char_major */
			i = register_chrdev_region(MKDEV(dvblo_char_major, 0), DVBLO_CHAR_DEVMAX, DVBLO_NAME);
			if(i < 0) {
				mprintk(KERN_ALERT, "failed to register char device number region (major=%d, count=%d): %d\n", 
					dvblo_char_major, DVBLO_CHAR_DEVMAX, i);
				rv = dvblo_char_major;
				break;
			}
		} else {
			/* dynamic allocation of the major device number */
			dev_t dev;
			i = alloc_chrdev_region(&dev, 0, DVBLO_CHAR_DEVMAX, DVBLO_NAME);
			if(i < 0) {
				mprintk(KERN_ALERT, "failed to allocate char device number region (count=%d): %d\n", 
					DVBLO_CHAR_DEVMAX, i);
				rv = dvblo_char_major;
				break;
			}
			dvblo_char_major = MAJOR(dev);
		}
#endif
		initlev++; /* 1 */
		
#if defined(USE_CDEV) && USE_CDEV != 0
		cdev_init(&dvblo_char_cdev, &dvblo_char_fops);
		// fops are assigned in cdev_init()
		//dvblo_char_cdev.ops = &dvblo_char_fops;
		dvblo_char_cdev.owner = THIS_MODULE;
		i = cdev_add(&dvblo_char_cdev, MKDEV(dvblo_char_major, 0), DVBLO_CHAR_DEVMAX);
		if(i < 0)
		{
			mprintk(KERN_ALERT, "failed to add char device: %d\n", i);
			rv = dvblo_char_major;
			break;
		}
#else
		/* NOTE: by setting <major> to 0 we request a dynamic major device number
		 */
		i = register_chrdev(dvblo_char_major, DVBLO_NAME, &dvblo_char_fops);
		if(i < 0)
		{
			mprintk(KERN_ALERT, "failed to register char device: %d\n", i);
			rv = i;
			break;
		} else if(dvblo_char_major == 0)
			dvblo_char_major = i;
		// else requested device major number was accepted
#endif
		initlev++; /* 2 */
		
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
		dvblo_class = class_create(THIS_MODULE, DVBLO_NAME);
#else
		dvblo_class = class_simple_create(THIS_MODULE, DVBLO_NAME);
#endif
		if(IS_ERR(dvblo_class)) {
			rv = PTR_ERR(dvblo_class);
			mprintk(KERN_ALERT, "failed to create class \"" DVBLO_NAME "\": %d\n", rv);
			break;
		}
		initlev++; /* 3 */
	} while(0);
	if(rv != 0)
		dvblo_char_exit(); // err cleanup
	return rv;
}
Esempio n. 7
0
static int dvblo_chardev_init(struct dvblo_chardev *chardev, struct dvblo_chardev_config *cfg)
{
	int rv = SUCCESS, i;
	
	if(chardev->used != 0) {
		rv = -EINVAL;
	} else {
		chardev->used = 1;
		chardev->initlev = 0;
		chardev->initdone = 0;
		do {
			dprintk(DBGLEV_ALL, "initializing char device with minor device number %u\n", chardev->minor);
			i = snprintf(chardev->name, sizeof(chardev->name), DVBLO_NAME "%d", chardev->minor);
			if(i < 0 || i >= sizeof(chardev->name)) {
				if(i < 0)
					rv = i;
				else
					rv = -ENOBUFS;
				break;
			}
			
			chardev->hcount = 0;
			chardev->cfg.ts_sz = DVBLO_TS_SZ;
			chardev->cfg.hwrbuf_sz = 20 * chardev->cfg.ts_sz;
			
			i = dvblo_adap_create(chardev->minor, cfg ? &cfg->dvbcfg : NULL, &chardev->dvblo);
			if(i < 0) {
				mprintk(KERN_ALERT, "failed to create virtual DVB adapter: %d\n", i);
				rv = i;
				break;
			}
			chardev->initlev++;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
			/* In 2.6.15, class_device_create() got a pointer to the parent device (if any) as its second param */ 
			chardev->clsdev = class_device_create(dvblo_class, NULL, MKDEV(dvblo_char_major, chardev->minor), NULL, chardev->name);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
			/* class_device_create() first appeared in 2.6.13 */
			chardev->clsdev = class_device_create(dvblo_class, MKDEV(dvblo_char_major, chardev->minor), NULL, chardev->name);
#else
			/* The old-style "simple" class API */
			chardev->clsdev = class_simple_device_add(dvblo_class, MKDEV(dvblo_char_major, chardev->minor), NULL, chardev->name);
#endif
			if(IS_ERR(chardev->clsdev)) {
				rv = PTR_ERR(chardev->clsdev);
				mprintk(KERN_ALERT, "failed to create device class \"%s\": %d\n", chardev->name, rv);
				break;
			}
			chardev->initlev++;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
			for(i=0; i<(sizeof(clsdev_attrs)/sizeof(clsdev_attrs[0])) && rv == SUCCESS; i++)
				rv = class_device_create_file(chardev->clsdev, &clsdev_attrs[i]);
			if(rv != SUCCESS)
				break;
#endif
			chardev->initlev++;
			mprintk(KERN_INFO, "added character device %s\n", chardev->name);
		} while(0);
		if(rv != 0)
			dvblo_chardev_release(chardev); // error cleanup
	}
	return rv;
}
Esempio n. 8
0
/* dvblo_char_write
 *
 * This function forwards TS packets written to the char device to the virtual DVB adapter
 */
static ssize_t dvblo_char_op_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
	ssize_t rv = 0, cbytes;
	struct dvblo_chardev_handle *handle = (struct dvblo_chardev_handle*) filp->private_data;
	size_t ts_sz = handle->chardev->cfg.ts_sz, wrleft, rdleft, cbytes_req, bytes_left;
	dprintk(DBGLEV_CHAR3, "filp=%p, buf=%p, len=%u, off=%p\n", filp, buf, len, off);
	/* we only accept complete TS packets */
	bytes_left = (len / ts_sz) * ts_sz;
	while(bytes_left > 0) {
		do {
			wrleft = handle->buf.wr < handle->buf.rd ? (handle->buf.rd - handle->buf.wr) : (handle->buf.size - handle->buf.wr);
			if(wrleft == 0) {
				/* we can't write anymore */
				if(rv == 0) {
					if((filp->f_flags & O_NONBLOCK) != 0)  
						rv = -EAGAIN; /* we were not able to write anything */
					else {
						///@todo support blocking operation
						rv = -ENOSYS;
					}
				}
			}
		} while(wrleft == 0 && rv >= 0);
		if(wrleft == 0 || rv < 0)
			break; /* no buffer space left */
		if((wrleft % ts_sz) != 0) {
			mprintk(KERN_ERR, "Lost TS packet boundary (write) in ring buffer. Please report this to the maintainer\n");
			rv = -EBADFD;
			break;
		}
		/* calc. how much we can write, at most */
		cbytes_req = bytes_left < wrleft ? bytes_left : wrleft;
		/* NOTE: copy_from_user() returns number of bytes that could not be copied.
		 * On success, this will be zero.
		 */
		if(copy_from_user(&handle->buf.base[handle->buf.wr], &buf[rv], cbytes_req) != 0) {
			rv = -EFAULT;
			break; /* error while copying */
		}
		dprintk(DBGLEV_CHAR3, "wr=%u, wrleft=%u, bytes_left=%u, cbytes_req=%u, rv=%d\n",
			handle->buf.wr, wrleft, bytes_left, cbytes_req, rv);
		/* update write offset (with wrap-around) */
		handle->buf.wr = (handle->buf.wr + cbytes_req) % handle->buf.size;
		rv += cbytes_req;
		/* @note At this point we could invoke a "virtual multiplexer" which reads from 
		 * different input streams
		 */
		rdleft = handle->buf.rd > handle->buf.wr ? (handle->buf.size - handle->buf.rd) : (handle->buf.wr - handle->buf.rd);
		if((rdleft % ts_sz) != 0) {
			mprintk(KERN_ERR, "Lost read TS packet boundary in ring buffer. Please report this to the maintainer\n");
			rv = -EBADFD;
			break;
		}
		cbytes_req = rdleft;
		cbytes = dvblo_adap_deliver_packets(handle->chardev->dvblo, &handle->buf.base[handle->buf.rd], cbytes_req);
		if(cbytes < 0) {
			rv = cbytes;
			break; /* failed while delivering TS packets to DVB adapter */
		} else if((cbytes % ts_sz) != 0) {
			rv = -EBADFD;
			break;
		}
		dprintk(DBGLEV_CHAR3, "rd=%u, rdleft=%u, cbytes_req=%u, cbytes=%d\n",
			handle->buf.rd, rdleft, cbytes_req, cbytes);
		/* update read offset (with wrap-around) */
		handle->buf.rd = (handle->buf.rd + cbytes) % handle->buf.size;
		bytes_left -= cbytes;
	}
	return rv;
}