/* * squarewave.c -- simple PWM generator for telepoint(tm) system based on the 8254 * controls two NARO BB servos, or the like (/dev/squarewave0 and /dev/squarewave1) */ #include #include /* printk() */ #include #include /* everything... */ #include /* error codes */ #include #include #include #include #include #include #define SW_BASE 0x260 #define SW_TIMER_0 0x260 #define SW_TIMER_1 0x261 #define SW_TIMER_2 0x262 #define SW_CONTROL 0x263 //#define SERVO_MIN 1041 /* HCL SW_INIT is used to initialize the speaker, this will have to be tweaked with */ #define SW 0xffff #define SW_MIN (SW / 200) // about 330 Hz, somewhere between a C and D #define SW_INIT (SW_MIN + (SW_MIN >> 1)) //#define SW_INIT (SW >> 2) #define DIVIDE_FACTOR 1041667 int squarewave_major = 0; int squarewave_servo2; int squarewave_servo1; int imode; void generate_wave(char *kbuf, int count); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) int squarewave_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *data) { len = sprintf(buf, "Servo setting: %i\nThrottle setting: %i\n", squarewave_servo2, squarewave_servo1); return len; } #else int squarewave_read_proc(char *buf, char **start, off_t offset, int len, int unused) { len = sprintf(buf, "Servo setting: %i\nThrottle setting: %i\n", squarewave_servo2, squarewave_servo1); return len; } #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) struct proc_dir_entry squarewave_proc_entry = { 0, 3, "squarewave", S_IFREG | S_IRUGO, 1, 0, 0, 0, NULL, &squarewave_read_proc, }; #endif static int squarewave_open(struct inode *inode, struct file *filp) { MOD_INC_USE_COUNT; return 0; } static int squarewave_release(struct inode *inode, struct file *filp) { MOD_DEC_USE_COUNT; return 0; } enum squarewave_modes {SW_CLOCK=0, SW_WAVE}; static ssize_t squarewave_write(struct file *filp, const char *buf, size_t count, loff_t *offset ) { int retval = count; //unsigned int writeval; //unsigned int countval; //unsigned char outval; int mode = MINOR(filp->f_dentry->d_inode->i_rdev); unsigned char *kbuf=kmalloc(count, GFP_KERNEL), *ptr; if (!kbuf) return -ENOMEM; /* memcpy_fromfs(kbuf, buf, count);*/ copy_from_user(kbuf, buf, count); ptr = kbuf; //printk("<1>squarewave minor: 0x%x\n", mode); switch(mode) { case SW_CLOCK: generate_wave(kbuf, count); /*while (count--) { writeval = *(ptr++); printk("<1>timer: servo2: reading in %0x\n", writeval); countval = writeval; printk("<1>timer: writing value %i\n", countval); outval = countval & 0xff; printk("<1>timer: writing LSB 0x%.2x\n", outval); outb(outval, SW_TIMER_0); outval = countval >> 8 & 0xff; printk("<1>timer: writing MSB 0x%.2x\n", outval); outb(outval, SW_TIMER_0); */ /* writeval = *(ptr++); count--; printk("<1>sq_wave_g: reading in %0x\n", writeval); countval = 4 * writeval; printk("<1>sq_wave_g: writing value %i\n", countval); outval = countval & 0xff; printk("<1>sq_wave_g: writing LSB 0x%.2x\n", outval); outb(outval, SW_TIMER_1); outval = countval >> 8 & 0xff; printk("<1>sq_wave_g: writing MSB 0x%.2x\n", outval); outb(outval, SW_TIMER_1);*/ //squarewave_servo2 = countval; //} break; /* case SW_THROTTLE: while (count--) { writeval = *(ptr++); countval = 4 * writeval + SERVO_MIN; countval = DIVIDE_FACTOR - writeval * ((double)DIVIDE_FACTOR / 255); printk("<1>squarewave led: writing value %0x\n", countval); outval = countval & 0xff; printk("<1>pwm led: writing LSB 0x%.2x\n", outval); outb(outval, PWM_TIMER_1); outval = (countval >> 8) & 0xff; printk("<1>pwm led: writing MSB 0x%.2x\n", outval); outb(outval, PWM_TIMER_1); pwm_servo1= countval; } break; */ default: /* no more modes defined by now */ retval = -EINVAL; break; } kfree(kbuf); return retval; } struct file_operations squarewave_fops = { write : squarewave_write, open : squarewave_open, release : squarewave_release }; int init_module(void) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) struct proc_dir_entry *ent; #endif int result = check_region(SW_BASE, 32); /* HCL changed to SW_BASE */ if (result) { printk(KERN_INFO "squarewave: can't get I/O address 0x%x\n", SW_BASE); return result; } /* HCL changed to SW_BASE */ request_region(SW_BASE, 32, "squarewave"); result = register_chrdev(squarewave_major, "squarewave", &squarewave_fops); if (result < 0) { printk(KERN_INFO "squarewave: can't get major number\n"); /* HCL changed to SW_BASE */ release_region(SW_BASE, 32); return result; } if (squarewave_major == 0) squarewave_major = result; /* dynamic major number assignment */ printk("<1>squarewave installed...\n"); /* HCL set up timer 0 for mode 0, interrupt on terminal count down */ outb(0x30, SW_CONTROL); /* outb(0xb1, PWM_TIMER_0); outb(0x28, PWM_TIMER_0); */ /* HCL initialize to highest setting, James and I discussed it and agreed on 1/2 second pulse, but that is 5.0+E5 Hz, which is higher than the max allowed in 16 bit, so I just set it to max ffff, can change if necessary */ outb(SW & 0xff, SW_TIMER_0); outb((SW >> 8) & 0xff, SW_TIMER_0); printk("<1>timer 0 initialized...\n"); /* HCL this sets up timer 1, will have to initialize this to something, not sure what this is right now, so just kept previous settings */ outb(0x76, SW_CONTROL); /* outb(0x1b, PWM_TIMER_2); outb(0x06, PWM_TIMER_2); */ outb(SW_MIN & 0xff , SW_TIMER_1); outb((SW_MIN >> 8) & 0xff, SW_TIMER_1); printk("<1>timer 1 initialized...\n"); squarewave_servo2 = 0x061b; /* set up timer 1 also to center of servo range (as with above servo) * this would be half-way */ /* outb(0x72, PWM_CONTROL); */ /* outb(0x1b, PWM_TIMER_1); outb(0x06, PWM_TIMER_1); */ // outb(LED_INIT & 0xff, PWM_TIMER_1); // outb((LED_INIT >> 8) & 0xff, PWM_TIMER_1); // printk("<1>timer 1 initialized...\n"); // pwm_servo1 = 0x061b; /* proc_register_dynamic(&proc_root, &pwm_proc_entry); */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) if ((ent = create_proc_entry("squarewave", S_IRUGO, NULL)) != NULL) { ent->read_proc = squarewave_read_proc; } #else proc_register(&proc_root, &squarewave_proc_entry); #endif return 0; } void cleanup_module(void) { unregister_chrdev(squarewave_major, "squarewave"); release_region(SW_BASE, 32); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) remove_proc_entry("squarewave", NULL); #else proc_unregister(&proc_root, squarewave_proc_entry.low_ino); #endif printk("<1>squarewave removed...\n"); } /* JJ convert the initial portion of a string to an integer. */ int atoi(char *s) { int i, n; n = 0; for (i = 0; s[i] >= '0' && s[i] <= '9'; ++i) n = 10 * n + s[i] - '0'; return n; } void generate_wave(char *kbuf, int count) { //unsigned int writeval; unsigned int countval; unsigned char outval; unsigned int waveval; //int i; /* int argc = 1; char **argv = (char **) kmalloc(sizeof(char *) *100, GFP_ATOMIC); char *p = strtok(kbuf, " "); //char *cPtr; argv[0] = ""; kbuf[count] = '\0'; while (p != NULL) { argv[argc] = p; argc++; p = strtok(NULL, " "); } */ char *p; kbuf[count] = '\0'; p = strtok(kbuf, " "); if (p != NULL) countval = atoi(p); else countval = 1; p = strtok(NULL, " "); if (p != NULL) waveval = atoi(p); else waveval = 350; //printk("<1>timer: servo2: reading in %0x\n", writeval); //countval = atoi(argv[1]); printk("<1>timer: writing value %i\n", countval); outval = countval & 0xff; printk("<1>timer: writing LSB 0x%.2x\n", outval); outb(outval, SW_TIMER_0); outval = countval >> 8 & 0xff; printk("<1>timer: writing MSB 0x%.2x\n", outval); outb(outval, SW_TIMER_0); //waveval = atoi(argv[2]); waveval = DIVIDE_FACTOR*(1.0/waveval); printk("<1>sq_wave_g: writing value %i\n", waveval); outval = waveval & 0xff; printk("<1>sq_wave_g: writing LSB 0x%.2x\n", outval); outb(outval, SW_TIMER_1); outval = waveval >> 8 & 0xff; printk("<1>sq_wave_g: writing MSB 0x%.2x\n", outval); outb(outval, SW_TIMER_1); udelay(countval); outb(0x00, SW_TIMER_1); outb(0x00, SW_TIMER_1); }