11da177e4SLinus Torvalds /* radio-aztech.c - Aztech radio card driver for Linux 2.2 21da177e4SLinus Torvalds * 3a4366af4SMauro Carvalho Chehab * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> 41da177e4SLinus Torvalds * Adapted to support the Video for Linux API by 51da177e4SLinus Torvalds * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by: 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * Quay Ly 81da177e4SLinus Torvalds * Donald Song 91da177e4SLinus Torvalds * Jason Lewis (jlewis@twilight.vtc.vsc.edu) 101da177e4SLinus Torvalds * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) 111da177e4SLinus Torvalds * William McGrath (wmcgrath@twilight.vtc.vsc.edu) 121da177e4SLinus Torvalds * 131da177e4SLinus Torvalds * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/ 141da177e4SLinus Torvalds * along with more information on the card itself. 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * History: 171da177e4SLinus Torvalds * 1999-02-24 Russell Kroll <rkroll@exploits.org> 181da177e4SLinus Torvalds * Fine tuning/VIDEO_TUNER_LOW 191da177e4SLinus Torvalds * Range expanded to 87-108 MHz (from 87.9-107.8) 201da177e4SLinus Torvalds * 211da177e4SLinus Torvalds * Notable changes from the original source: 221da177e4SLinus Torvalds * - includes stripped down to the essentials 231da177e4SLinus Torvalds * - for loops used as delays replaced with udelay() 241da177e4SLinus Torvalds * - #defines removed, changed to static values 251da177e4SLinus Torvalds * - tuning structure changed - no more character arrays, other changes 261da177e4SLinus Torvalds */ 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds #include <linux/module.h> /* Modules */ 291da177e4SLinus Torvalds #include <linux/init.h> /* Initdata */ 30fb911ee8SPeter Osterlund #include <linux/ioport.h> /* request_region */ 311da177e4SLinus Torvalds #include <linux/delay.h> /* udelay */ 321da177e4SLinus Torvalds #include <asm/io.h> /* outb, outb_p */ 331da177e4SLinus Torvalds #include <asm/uaccess.h> /* copy to/from user */ 34a4366af4SMauro Carvalho Chehab #include <linux/videodev2.h> /* kernel radio structs */ 355e87efa3SMauro Carvalho Chehab #include <media/v4l2-common.h> 361da177e4SLinus Torvalds 37a4366af4SMauro Carvalho Chehab #include <linux/version.h> /* for KERNEL_VERSION MACRO */ 38a4366af4SMauro Carvalho Chehab #define RADIO_VERSION KERNEL_VERSION(0,0,2) 39a4366af4SMauro Carvalho Chehab 40a4366af4SMauro Carvalho Chehab static struct v4l2_queryctrl radio_qctrl[] = { 41a4366af4SMauro Carvalho Chehab { 42a4366af4SMauro Carvalho Chehab .id = V4L2_CID_AUDIO_MUTE, 43a4366af4SMauro Carvalho Chehab .name = "Mute", 44a4366af4SMauro Carvalho Chehab .minimum = 0, 45a4366af4SMauro Carvalho Chehab .maximum = 1, 46a4366af4SMauro Carvalho Chehab .default_value = 1, 47a4366af4SMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_BOOLEAN, 48a4366af4SMauro Carvalho Chehab },{ 49a4366af4SMauro Carvalho Chehab .id = V4L2_CID_AUDIO_VOLUME, 50a4366af4SMauro Carvalho Chehab .name = "Volume", 51a4366af4SMauro Carvalho Chehab .minimum = 0, 52a4366af4SMauro Carvalho Chehab .maximum = 0xff, 53a4366af4SMauro Carvalho Chehab .step = 1, 54a4366af4SMauro Carvalho Chehab .default_value = 0xff, 55a4366af4SMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_INTEGER, 56a4366af4SMauro Carvalho Chehab } 57a4366af4SMauro Carvalho Chehab }; 58a4366af4SMauro Carvalho Chehab 591da177e4SLinus Torvalds /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */ 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds #ifndef CONFIG_RADIO_AZTECH_PORT 621da177e4SLinus Torvalds #define CONFIG_RADIO_AZTECH_PORT -1 631da177e4SLinus Torvalds #endif 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds static int io = CONFIG_RADIO_AZTECH_PORT; 661da177e4SLinus Torvalds static int radio_nr = -1; 671da177e4SLinus Torvalds static int radio_wait_time = 1000; 683593cab5SIngo Molnar static struct mutex lock; 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds struct az_device 711da177e4SLinus Torvalds { 721da177e4SLinus Torvalds int curvol; 731da177e4SLinus Torvalds unsigned long curfreq; 741da177e4SLinus Torvalds int stereo; 751da177e4SLinus Torvalds }; 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds static int volconvert(int level) 781da177e4SLinus Torvalds { 791da177e4SLinus Torvalds level>>=14; /* Map 16bits down to 2 bit */ 801da177e4SLinus Torvalds level&=3; 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds /* convert to card-friendly values */ 831da177e4SLinus Torvalds switch (level) 841da177e4SLinus Torvalds { 851da177e4SLinus Torvalds case 0: 861da177e4SLinus Torvalds return 0; 871da177e4SLinus Torvalds case 1: 881da177e4SLinus Torvalds return 1; 891da177e4SLinus Torvalds case 2: 901da177e4SLinus Torvalds return 4; 911da177e4SLinus Torvalds case 3: 921da177e4SLinus Torvalds return 5; 931da177e4SLinus Torvalds } 941da177e4SLinus Torvalds return 0; /* Quieten gcc */ 951da177e4SLinus Torvalds } 961da177e4SLinus Torvalds 971da177e4SLinus Torvalds static void send_0_byte (struct az_device *dev) 981da177e4SLinus Torvalds { 991da177e4SLinus Torvalds udelay(radio_wait_time); 1001da177e4SLinus Torvalds outb_p(2+volconvert(dev->curvol), io); 1011da177e4SLinus Torvalds outb_p(64+2+volconvert(dev->curvol), io); 1021da177e4SLinus Torvalds } 1031da177e4SLinus Torvalds 1041da177e4SLinus Torvalds static void send_1_byte (struct az_device *dev) 1051da177e4SLinus Torvalds { 1061da177e4SLinus Torvalds udelay (radio_wait_time); 1071da177e4SLinus Torvalds outb_p(128+2+volconvert(dev->curvol), io); 1081da177e4SLinus Torvalds outb_p(128+64+2+volconvert(dev->curvol), io); 1091da177e4SLinus Torvalds } 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds static int az_setvol(struct az_device *dev, int vol) 1121da177e4SLinus Torvalds { 1133593cab5SIngo Molnar mutex_lock(&lock); 1141da177e4SLinus Torvalds outb (volconvert(vol), io); 1153593cab5SIngo Molnar mutex_unlock(&lock); 1161da177e4SLinus Torvalds return 0; 1171da177e4SLinus Torvalds } 1181da177e4SLinus Torvalds 1191da177e4SLinus Torvalds /* thanks to Michael Dwyer for giving me a dose of clues in 1201da177e4SLinus Torvalds * the signal strength department.. 1211da177e4SLinus Torvalds * 1221da177e4SLinus Torvalds * This card has a stereo bit - bit 0 set = mono, not set = stereo 1231da177e4SLinus Torvalds * It also has a "signal" bit - bit 1 set = bad signal, not set = good 1241da177e4SLinus Torvalds * 1251da177e4SLinus Torvalds */ 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds static int az_getsigstr(struct az_device *dev) 1281da177e4SLinus Torvalds { 1291da177e4SLinus Torvalds if (inb(io) & 2) /* bit set = no signal present */ 1301da177e4SLinus Torvalds return 0; 1311da177e4SLinus Torvalds return 1; /* signal present */ 1321da177e4SLinus Torvalds } 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds static int az_getstereo(struct az_device *dev) 1351da177e4SLinus Torvalds { 1361da177e4SLinus Torvalds if (inb(io) & 1) /* bit set = mono */ 1371da177e4SLinus Torvalds return 0; 1381da177e4SLinus Torvalds return 1; /* stereo */ 1391da177e4SLinus Torvalds } 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds static int az_setfreq(struct az_device *dev, unsigned long frequency) 1421da177e4SLinus Torvalds { 1431da177e4SLinus Torvalds int i; 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds frequency += 171200; /* Add 10.7 MHz IF */ 1461da177e4SLinus Torvalds frequency /= 800; /* Convert to 50 kHz units */ 1471da177e4SLinus Torvalds 1483593cab5SIngo Molnar mutex_lock(&lock); 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds send_0_byte (dev); /* 0: LSB of frequency */ 1511da177e4SLinus Torvalds 1521da177e4SLinus Torvalds for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ 1531da177e4SLinus Torvalds if (frequency & (1 << i)) 1541da177e4SLinus Torvalds send_1_byte (dev); 1551da177e4SLinus Torvalds else 1561da177e4SLinus Torvalds send_0_byte (dev); 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds send_0_byte (dev); /* 14: test bit - always 0 */ 1591da177e4SLinus Torvalds send_0_byte (dev); /* 15: test bit - always 0 */ 1601da177e4SLinus Torvalds send_0_byte (dev); /* 16: band data 0 - always 0 */ 1611da177e4SLinus Torvalds if (dev->stereo) /* 17: stereo (1 to enable) */ 1621da177e4SLinus Torvalds send_1_byte (dev); 1631da177e4SLinus Torvalds else 1641da177e4SLinus Torvalds send_0_byte (dev); 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds send_1_byte (dev); /* 18: band data 1 - unknown */ 1671da177e4SLinus Torvalds send_0_byte (dev); /* 19: time base - always 0 */ 1681da177e4SLinus Torvalds send_0_byte (dev); /* 20: spacing (0 = 25 kHz) */ 1691da177e4SLinus Torvalds send_1_byte (dev); /* 21: spacing (1 = 25 kHz) */ 1701da177e4SLinus Torvalds send_0_byte (dev); /* 22: spacing (0 = 25 kHz) */ 1711da177e4SLinus Torvalds send_1_byte (dev); /* 23: AM/FM (FM = 1, always) */ 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds /* latch frequency */ 1741da177e4SLinus Torvalds 1751da177e4SLinus Torvalds udelay (radio_wait_time); 1761da177e4SLinus Torvalds outb_p(128+64+volconvert(dev->curvol), io); 1771da177e4SLinus Torvalds 1783593cab5SIngo Molnar mutex_unlock(&lock); 1791da177e4SLinus Torvalds 1801da177e4SLinus Torvalds return 0; 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds 1831da177e4SLinus Torvalds static int az_do_ioctl(struct inode *inode, struct file *file, 1841da177e4SLinus Torvalds unsigned int cmd, void *arg) 1851da177e4SLinus Torvalds { 1861da177e4SLinus Torvalds struct video_device *dev = video_devdata(file); 1871da177e4SLinus Torvalds struct az_device *az = dev->priv; 1881da177e4SLinus Torvalds 1891da177e4SLinus Torvalds switch(cmd) 1901da177e4SLinus Torvalds { 191a4366af4SMauro Carvalho Chehab case VIDIOC_QUERYCAP: 1921da177e4SLinus Torvalds { 193a4366af4SMauro Carvalho Chehab struct v4l2_capability *v = arg; 1941da177e4SLinus Torvalds memset(v,0,sizeof(*v)); 195a4366af4SMauro Carvalho Chehab strlcpy(v->driver, "radio-aztech", sizeof (v->driver)); 196a4366af4SMauro Carvalho Chehab strlcpy(v->card, "Aztech Radio", sizeof (v->card)); 197a4366af4SMauro Carvalho Chehab sprintf(v->bus_info,"ISA"); 198a4366af4SMauro Carvalho Chehab v->version = RADIO_VERSION; 199a4366af4SMauro Carvalho Chehab v->capabilities = V4L2_CAP_TUNER; 200a4366af4SMauro Carvalho Chehab 2011da177e4SLinus Torvalds return 0; 2021da177e4SLinus Torvalds } 203a4366af4SMauro Carvalho Chehab case VIDIOC_G_TUNER: 2041da177e4SLinus Torvalds { 205a4366af4SMauro Carvalho Chehab struct v4l2_tuner *v = arg; 206a4366af4SMauro Carvalho Chehab 207a4366af4SMauro Carvalho Chehab if (v->index > 0) 2081da177e4SLinus Torvalds return -EINVAL; 209a4366af4SMauro Carvalho Chehab 210a4366af4SMauro Carvalho Chehab memset(v,0,sizeof(*v)); 211a4366af4SMauro Carvalho Chehab strcpy(v->name, "FM"); 212a4366af4SMauro Carvalho Chehab v->type = V4L2_TUNER_RADIO; 213a4366af4SMauro Carvalho Chehab 2141da177e4SLinus Torvalds v->rangelow=(87*16000); 2151da177e4SLinus Torvalds v->rangehigh=(108*16000); 216a4366af4SMauro Carvalho Chehab v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; 217a4366af4SMauro Carvalho Chehab v->capability=V4L2_TUNER_CAP_LOW; 2181da177e4SLinus Torvalds if(az_getstereo(az)) 219a4366af4SMauro Carvalho Chehab v->audmode = V4L2_TUNER_MODE_STEREO; 220a4366af4SMauro Carvalho Chehab else 221a4366af4SMauro Carvalho Chehab v->audmode = V4L2_TUNER_MODE_MONO; 222a4366af4SMauro Carvalho Chehab v->signal=0xFFFF*az_getsigstr(az); 223a4366af4SMauro Carvalho Chehab 2241da177e4SLinus Torvalds return 0; 2251da177e4SLinus Torvalds } 226a4366af4SMauro Carvalho Chehab case VIDIOC_S_TUNER: 2271da177e4SLinus Torvalds { 228a4366af4SMauro Carvalho Chehab struct v4l2_tuner *v = arg; 229a4366af4SMauro Carvalho Chehab 230a4366af4SMauro Carvalho Chehab if (v->index > 0) 2311da177e4SLinus Torvalds return -EINVAL; 232a4366af4SMauro Carvalho Chehab 2331da177e4SLinus Torvalds return 0; 2341da177e4SLinus Torvalds } 235a4366af4SMauro Carvalho Chehab case VIDIOC_S_FREQUENCY: 2361da177e4SLinus Torvalds { 237a4366af4SMauro Carvalho Chehab struct v4l2_frequency *f = arg; 238a4366af4SMauro Carvalho Chehab 239a4366af4SMauro Carvalho Chehab az->curfreq = f->frequency; 2401da177e4SLinus Torvalds az_setfreq(az, az->curfreq); 2411da177e4SLinus Torvalds return 0; 2421da177e4SLinus Torvalds } 243a4366af4SMauro Carvalho Chehab case VIDIOC_G_FREQUENCY: 2441da177e4SLinus Torvalds { 245a4366af4SMauro Carvalho Chehab struct v4l2_frequency *f = arg; 2461da177e4SLinus Torvalds 247a4366af4SMauro Carvalho Chehab f->type = V4L2_TUNER_RADIO; 248a4366af4SMauro Carvalho Chehab f->frequency = az->curfreq; 249a4366af4SMauro Carvalho Chehab 2501da177e4SLinus Torvalds return 0; 2511da177e4SLinus Torvalds } 252a4366af4SMauro Carvalho Chehab 253a4366af4SMauro Carvalho Chehab case VIDIOC_QUERYCTRL: 254a4366af4SMauro Carvalho Chehab { 255a4366af4SMauro Carvalho Chehab struct v4l2_queryctrl *qc = arg; 256a4366af4SMauro Carvalho Chehab int i; 257a4366af4SMauro Carvalho Chehab 258a4366af4SMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 259a4366af4SMauro Carvalho Chehab if (qc->id && qc->id == radio_qctrl[i].id) { 260a4366af4SMauro Carvalho Chehab memcpy(qc, &(radio_qctrl[i]), 261a4366af4SMauro Carvalho Chehab sizeof(*qc)); 262a4366af4SMauro Carvalho Chehab return (0); 263a4366af4SMauro Carvalho Chehab } 264a4366af4SMauro Carvalho Chehab } 265a4366af4SMauro Carvalho Chehab return -EINVAL; 266a4366af4SMauro Carvalho Chehab } 267a4366af4SMauro Carvalho Chehab case VIDIOC_G_CTRL: 268a4366af4SMauro Carvalho Chehab { 269a4366af4SMauro Carvalho Chehab struct v4l2_control *ctrl= arg; 270a4366af4SMauro Carvalho Chehab 271a4366af4SMauro Carvalho Chehab switch (ctrl->id) { 272a4366af4SMauro Carvalho Chehab case V4L2_CID_AUDIO_MUTE: 273a4366af4SMauro Carvalho Chehab if (az->curvol==0) 274a4366af4SMauro Carvalho Chehab ctrl->value=1; 275a4366af4SMauro Carvalho Chehab else 276a4366af4SMauro Carvalho Chehab ctrl->value=0; 277a4366af4SMauro Carvalho Chehab return (0); 278a4366af4SMauro Carvalho Chehab case V4L2_CID_AUDIO_VOLUME: 279a4366af4SMauro Carvalho Chehab ctrl->value=az->curvol * 6554; 280a4366af4SMauro Carvalho Chehab return (0); 281a4366af4SMauro Carvalho Chehab } 282a4366af4SMauro Carvalho Chehab return -EINVAL; 283a4366af4SMauro Carvalho Chehab } 284a4366af4SMauro Carvalho Chehab case VIDIOC_S_CTRL: 285a4366af4SMauro Carvalho Chehab { 286a4366af4SMauro Carvalho Chehab struct v4l2_control *ctrl= arg; 287a4366af4SMauro Carvalho Chehab 288a4366af4SMauro Carvalho Chehab switch (ctrl->id) { 289a4366af4SMauro Carvalho Chehab case V4L2_CID_AUDIO_MUTE: 290a4366af4SMauro Carvalho Chehab if (ctrl->value) { 291a4366af4SMauro Carvalho Chehab az_setvol(az,0); 292a4366af4SMauro Carvalho Chehab } else { 293a4366af4SMauro Carvalho Chehab az_setvol(az,az->curvol); 294a4366af4SMauro Carvalho Chehab } 295a4366af4SMauro Carvalho Chehab return (0); 296a4366af4SMauro Carvalho Chehab case V4L2_CID_AUDIO_VOLUME: 297a4366af4SMauro Carvalho Chehab az_setvol(az,ctrl->value); 298a4366af4SMauro Carvalho Chehab return (0); 299a4366af4SMauro Carvalho Chehab } 300a4366af4SMauro Carvalho Chehab return -EINVAL; 301a4366af4SMauro Carvalho Chehab } 302a4366af4SMauro Carvalho Chehab 3031da177e4SLinus Torvalds default: 304a4366af4SMauro Carvalho Chehab return v4l_compat_translate_ioctl(inode,file,cmd,arg, 305a4366af4SMauro Carvalho Chehab az_do_ioctl); 3061da177e4SLinus Torvalds } 3071da177e4SLinus Torvalds } 3081da177e4SLinus Torvalds 3091da177e4SLinus Torvalds static int az_ioctl(struct inode *inode, struct file *file, 3101da177e4SLinus Torvalds unsigned int cmd, unsigned long arg) 3111da177e4SLinus Torvalds { 3121da177e4SLinus Torvalds return video_usercopy(inode, file, cmd, arg, az_do_ioctl); 3131da177e4SLinus Torvalds } 3141da177e4SLinus Torvalds 3151da177e4SLinus Torvalds static struct az_device aztech_unit; 3161da177e4SLinus Torvalds 3171da177e4SLinus Torvalds static struct file_operations aztech_fops = { 3181da177e4SLinus Torvalds .owner = THIS_MODULE, 3191da177e4SLinus Torvalds .open = video_exclusive_open, 3201da177e4SLinus Torvalds .release = video_exclusive_release, 3211da177e4SLinus Torvalds .ioctl = az_ioctl, 3220d0fbf81SArnd Bergmann .compat_ioctl = v4l_compat_ioctl32, 3231da177e4SLinus Torvalds .llseek = no_llseek, 3241da177e4SLinus Torvalds }; 3251da177e4SLinus Torvalds 3261da177e4SLinus Torvalds static struct video_device aztech_radio= 3271da177e4SLinus Torvalds { 3281da177e4SLinus Torvalds .owner = THIS_MODULE, 3291da177e4SLinus Torvalds .name = "Aztech radio", 3301da177e4SLinus Torvalds .type = VID_TYPE_TUNER, 3311da177e4SLinus Torvalds .hardware = VID_HARDWARE_AZTECH, 3321da177e4SLinus Torvalds .fops = &aztech_fops, 3331da177e4SLinus Torvalds }; 3341da177e4SLinus Torvalds 3351da177e4SLinus Torvalds static int __init aztech_init(void) 3361da177e4SLinus Torvalds { 3371da177e4SLinus Torvalds if(io==-1) 3381da177e4SLinus Torvalds { 3391da177e4SLinus Torvalds printk(KERN_ERR "You must set an I/O address with io=0x???\n"); 3401da177e4SLinus Torvalds return -EINVAL; 3411da177e4SLinus Torvalds } 3421da177e4SLinus Torvalds 3431da177e4SLinus Torvalds if (!request_region(io, 2, "aztech")) 3441da177e4SLinus Torvalds { 3451da177e4SLinus Torvalds printk(KERN_ERR "aztech: port 0x%x already in use\n", io); 3461da177e4SLinus Torvalds return -EBUSY; 3471da177e4SLinus Torvalds } 3481da177e4SLinus Torvalds 3493593cab5SIngo Molnar mutex_init(&lock); 3501da177e4SLinus Torvalds aztech_radio.priv=&aztech_unit; 3511da177e4SLinus Torvalds 3521da177e4SLinus Torvalds if(video_register_device(&aztech_radio, VFL_TYPE_RADIO, radio_nr)==-1) 3531da177e4SLinus Torvalds { 3541da177e4SLinus Torvalds release_region(io,2); 3551da177e4SLinus Torvalds return -EINVAL; 3561da177e4SLinus Torvalds } 3571da177e4SLinus Torvalds 3581da177e4SLinus Torvalds printk(KERN_INFO "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n"); 3591da177e4SLinus Torvalds /* mute card - prevents noisy bootups */ 3601da177e4SLinus Torvalds outb (0, io); 3611da177e4SLinus Torvalds return 0; 3621da177e4SLinus Torvalds } 3631da177e4SLinus Torvalds 3641da177e4SLinus Torvalds MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); 3651da177e4SLinus Torvalds MODULE_DESCRIPTION("A driver for the Aztech radio card."); 3661da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 3671da177e4SLinus Torvalds 3681da177e4SLinus Torvalds module_param(io, int, 0); 3691da177e4SLinus Torvalds module_param(radio_nr, int, 0); 3701da177e4SLinus Torvalds MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)"); 3711da177e4SLinus Torvalds 3721da177e4SLinus Torvalds static void __exit aztech_cleanup(void) 3731da177e4SLinus Torvalds { 3741da177e4SLinus Torvalds video_unregister_device(&aztech_radio); 3751da177e4SLinus Torvalds release_region(io,2); 3761da177e4SLinus Torvalds } 3771da177e4SLinus Torvalds 3781da177e4SLinus Torvalds module_init(aztech_init); 3791da177e4SLinus Torvalds module_exit(aztech_cleanup); 380