- 论坛徽章:
- 0
|
给DP-1581开发版写了个Linux下的USB驱动
DP-1581是一款USB2.0开发开发套件,处理器为Philips的增强型51单片机P89C61x2,USB接口芯片为Philips的ISP1581。板子的相关资料见http://www.zlgmcu.com/tools/kaifaban/dp-1581.asp。
1 固件部分
固件是USB客户端设备上工作的程序,一般运行在单片机上,控制USB接口芯片实现USB协议第9章规定的各种标准和厂商请求以及传输数据。这里使用开发板自带的一个固件程序“USB按键、时钟演示程序”,本固件里除了控制端点外提供了4个usb端点,中断IN和OUT以及批量IN和OUT端点,读取板子上的按键并通过中断IN端点usb传给主机,读取中断OUT端点数据并输出到LED显示。本文中修改为都是用批量传输。固件工程使用Keil C开发,对原始固件修改如下:
- kernel.c中
- 94行改为read_endpoint(4,8,Endpt_FIFO);
- 107行改为write_endpoint(5,4,Endpt_FIFO);
复制代码
编译后下载进单片机即可。
2 驱动部分
驱动基于2.6.13内核。内核存在一个叫做USB Core的子系统,提供一组API支持USB主控制器和设备,见图1(来自Programming Guide for Linux USB Device Drivers)。具体API及USB设备驱动开发参考LDD3第13章(附件中带)。
图1
内核为USB驱动的开发提供了一个框架\drivers\usb\usb-skeleton.c。里面使用批量传输,不用修改即可使用,本文里已知固件里的端点信息,对驱动的probe函数做了简化。代码如下:
- /*isp1581.c
- * based on USB Skeleton driver - 2.0 by xdkui
- *
- *
- * This driver is based on the 2.6.3 version of drivers/usb/usb-isp1581eton.c
- * but has been rewritten to be easy to read and use, as no locks are now
- * needed anymore.
- *
- */
- #include <linux/config.h>
- #include <linux/kernel.h>
- #include <linux/errno.h>
- #include <linux/init.h>
- #include <linux/slab.h>
- #include <linux/module.h>
- #include <linux/kref.h>
- #include <asm/uaccess.h>
- #include <linux/usb.h>
- #define USB_SKEL_VENDOR_ID 0x04cc //厂商ID
- #define USB_SKEL_PRODUCT_ID 0x1b49 //产品ID
- static struct usb_device_id isp1581_table [] = {
- { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
- { }
- };
- MODULE_DEVICE_TABLE (usb, isp1581_table);
- /* Get a minor range for your devices from the usb maintainer */
- #define USB_ISP1581_MINOR_BASE 192 //USB次设备号,每个驱动分配16个次设备号
- /* Structure to hold all of our device specific stuff */
- struct usb_isp1581 {
- struct usb_device * udev; /* the usb device for this device */
- struct usb_interface * interface; /* the interface for this device */
- unsigned char * bulk_in_buffer; /* the buffer to receive data */
- size_t bulk_in_size; /* the size of the receive buffer */
- __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
- __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
- struct kref kref;
- };
- #define to_isp1581_dev(d) container_of(d, struct usb_isp1581, kref)
- static struct usb_driver isp1581_driver;
- static void isp1581_delete(struct kref *kref)
- {
- struct usb_isp1581 *dev = to_isp1581_dev(kref);
- usb_put_dev(dev->udev);
- kfree (dev->bulk_in_buffer);
- kfree (dev);
- }
- static int isp1581_open(struct inode *inode, struct file *file)
- {
- struct usb_isp1581 *dev;
- struct usb_interface *interface;
- int subminor;
- int retval = 0;
- subminor = iminor(inode);
- interface = usb_find_interface(&isp1581_driver, subminor);
- if (!interface) {
- err ("%s - error, can't find device for minor %d",
- __FUNCTION__, subminor);
- retval = -ENODEV;
- goto exit;
- }
- dev = usb_get_intfdata(interface);
- if (!dev) {
- retval = -ENODEV;
- goto exit;
- }
- /* increment our usage count for the device */
- kref_get(&dev->kref);
- /* save our object in the file's private structure */
- file->private_data = dev;
- exit:
- return retval;
- }
- static int isp1581_release(struct inode *inode, struct file *file)
- {
- struct usb_isp1581 *dev;
- dev = (struct usb_isp1581 *)file->private_data;
- if (dev == NULL)
- return -ENODEV;
- kref_put(&dev->kref, isp1581_delete);
- return 0;
- }
- static ssize_t isp1581_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
- //通过批量传输读取开发板上的按键
- {
- struct usb_isp1581 *dev;
- int retval = 0;
- int bytes_read;
- printk(KERN_ERR "enter isp1581_read\n");
- dev = (struct usb_isp1581 *)file->private_data;
-
- /* do a blocking bulk read to get data from the device */
- retval = usb_bulk_msg(dev->udev,
- usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
- dev->bulk_in_buffer,
- min(dev->bulk_in_size, count),
- &bytes_read, 5*HZ);
- if (!retval) {
- if (copy_to_user(buffer, dev->bulk_in_buffer, bytes_read))
- retval = -EFAULT;
- else
- retval = bytes_read;
- }
- return retval;
- }
- static void isp1581_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
- //写操作的回掉函数,在中断环境下被调用
- {
- struct usb_isp1581 *dev;
- dev = (struct usb_isp1581 *)urb->context;
- /* sync/async unlink faults aren't errors */
- if (urb->status &&
- !(urb->status == -ENOENT ||
- urb->status == -ECONNRESET ||
- urb->status == -ESHUTDOWN)) {
- dbg("%s - nonzero write bulk status received: %d",
- __FUNCTION__, urb->status);
- }
- /* free up our allocated buffer */
- usb_buffer_free(urb->dev, urb->transfer_buffer_length,
- urb->transfer_buffer, urb->transfer_dma);
- }
- static ssize_t isp1581_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos)
- //分配urb,填充并提交给USB core
- {
- struct usb_isp1581 *dev;
- int retval = 0;
- struct urb *urb = NULL;
- char *buf = NULL;
- dev = (struct usb_isp1581 *)file->private_data;
- if (count == 0)
- goto exit;
- /* create a urb, and a buffer for it, and copy the data to the urb */
- urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb) {
- retval = -ENOMEM;
- goto error;
- }
- buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);
- if (!buf) {
- retval = -ENOMEM;
- goto error;
- }
- if (copy_from_user(buf, user_buffer, count)) {
- retval = -EFAULT;
- goto error;
- }
- /* initialize the urb properly */
- usb_fill_bulk_urb(urb, dev->udev,
- usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
- buf, count, isp1581_write_bulk_callback, dev);
- urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- /* send the data out the bulk port */
- retval = usb_submit_urb(urb, GFP_KERNEL);
- if (retval) {
- err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
- goto error;
- }
- /* release our reference to this urb, the USB core will eventually free it entirely */
- usb_free_urb(urb);
- exit:
- return count;
- error:
- usb_buffer_free(dev->udev, count, buf, urb->transfer_dma);
- usb_free_urb(urb);
- return retval;
- }
- static struct file_operations isp1581_fops = {
- .owner = THIS_MODULE,
- .read = isp1581_read,
- .write = isp1581_write,
- .open = isp1581_open,
- .release = isp1581_release,
- };
- /*
- * usb class driver info in order to get a minor number from the usb core,
- * and to have the device registered with devfs and the driver core
- */
- static struct usb_class_driver isp1581_class = {
- .name = "isp1581",
- .fops = &isp1581_fops,
- .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
- .minor_base = USB_ISP1581_MINOR_BASE,
- };
- static int isp1581_probe(struct usb_interface *interface, const struct usb_device_id *id)
- //当有厂商ID,产品ID和USB_SKEL_VENDOR_ID,USB_SKEL_PRODUCT_ID匹配的时//候本函数被USB Core调用
- {
- struct usb_isp1581 *dev = NULL;
- /*struct usb_host_interface *iface_desc;
- struct usb_endpoint_descriptor *endpoint;
- size_t buffer_size;
- int i;*/
- int retval = -ENOMEM;
- /* allocate memory for our device state and initialize it */
- dev = kmalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL) {
- err("Out of memory");
- goto error;
- }
- memset(dev, 0x00, sizeof(*dev));
- kref_init(&dev->kref);
- dev->udev = usb_get_dev(interface_to_usbdev(interface));
- dev->interface = interface;
- /* set up the endpoint information */
- /* use only the first bulk-in and bulk-out endpoints */
- //do not search, i know the bulk addr, see Keil C program...
- dev->bulk_in_endpointAddr=0x82;//批量输入端点
- dev->bulk_out_endpointAddr=0x02;//批量输出端点
- dev->bulk_in_size = 64;
- dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL);
- if (!dev->bulk_in_buffer) {
- err("Could not allocate bulk_in_buffer");
- goto error;
- }
- /* save our data pointer in this interface device */
- usb_set_intfdata(interface, dev);
- /* we can register the device now, as it is ready */
- retval = usb_register_dev(interface, &isp1581_class);
- if (retval) {
- /* something prevented us from registering this driver */
- err("Not able to get a minor for this device.");
- usb_set_intfdata(interface, NULL);
- goto error;
- }
- /* let the user know what node this device is now attached to */
- info("USB ISP1581 device now attached to -%d", interface->minor);
- return 0;
- error:
- if (dev)
- kref_put(&dev->kref, isp1581_delete);
- return retval;
- }
- static void isp1581_disconnect(struct usb_interface *interface)
- //设备断开时被调用
- {
- struct usb_isp1581 *dev;
- int minor = interface->minor;
- /* prevent isp1581_open() from racing isp1581_disconnect() */
- lock_kernel();
- dev = usb_get_intfdata(interface);
- usb_set_intfdata(interface, NULL);
- /* give back our minor */
- usb_deregister_dev(interface, &isp1581_class);
- unlock_kernel();
- /* decrement our usage count */
- kref_put(&dev->kref, isp1581_delete);
- info("ISP1581 #%d now disconnected", minor);
- }
- static struct usb_driver isp1581_driver = {
- .owner = THIS_MODULE,
- .name = "isp1581",
- .probe = isp1581_probe,
- .disconnect = isp1581_disconnect,
- .id_table = isp1581_table,
- };
- static int __init usb_isp1581_init(void)
- {
- int result;
- /* register this driver with the USB subsystem */
- result = usb_register(&isp1581_driver);
- if (result)
- err("usb_register failed. Error number %d", result);
- return result;
- }
- static void __exit usb_isp1581_exit(void)
- {
- /* deregister this driver with the USB subsystem */
- usb_deregister(&isp1581_driver);
- }
- module_init (usb_isp1581_init);
- module_exit (usb_isp1581_exit);
- MODULE_LICENSE("GPL");
复制代码
3 用户态测试程序
首先建立设备文件mknod /dev/usb/isp1581 c 180 192。然后运行本程序通过USB读写数据。如:
- #test r 读取开发板的按键信息并显示
- #test w 12345678 则写8个数字到开发板并显示在LED。
复制代码
代码如下:
- /*user mode program for test isp1581 driver
- *first make a dev node
- * #mknod /dev/usb/isp1581 c 180 192
- */
- #include <stdio.h>
- #include <strings.h>
- #include <fcntl.h>
- void usage(char * name)
- {
- printf("Usage:%s {r|w num}\n",name);
- printf("\tr\tread 8 bytes from isp1581\n");
- printf("\tw\twrite 8 bytes(num) to isp1581\n");
- }
- void str2num(char * str,char * num,int n)
- {
- int i=0;
- bzero(num,n);
- while(n && *str)
- {
- char c=*str;
- if(c<='9' && c>='0')
- num[i]=c-'0';
- else if(c<='f' && c>='a')
- num[i]=c-'a'+10;
- else if(c<='F' && c>='A')
- num[i]=c-'A'+10;
- else
- num[i]=0;
- n--;
- str++;
- i++;
- }
- }
- int main(int argc,char **argv)
- {
- int fd=0;
- unsigned char buf[8];
- int i,len=0;
- if(argc<2)
- {
- usage(argv[0]);
- return 0;
- }
- fd=open("/dev/usb/isp1581",O_RDWR);
- if(fd==-1)
- {
- perror("open error");
- return -1;
- }
- if(argv[1][0]=='r' && argc==2)
- {
- bzero(buf,sizeof(buf));
- len=read(fd,buf,sizeof(buf));
- if(len==-1)
- perror("read error");
- else
- {
- printf("readed %d bytes:",len);
- for(i=0;i<len;i++)
- printf("%X\t",buf[i]);
- printf("\n");
- }
- }
- else if(argv[1][0]=='w' && argc==3)
- {
- str2num(argv[2],buf,sizeof(buf));
- len=write(fd,buf,sizeof(buf));
- if(len==-1)
- perror("write error");
- else
- printf("wrote %d bytes\n",len);
- }
- else
- usage(argv[0]);
- if(fd)
- close(fd);
- return 0;
- }
复制代码
4 Makefile
Makefile如下:
- obj-m := isp1581.o
- KERNELDIR ?=/usr/src/linux-2.6.13
- PWD := $(shell pwd)
- all:
- $(MAKE) -C $(KERNELDIR) M=$(PWD)
- $(CC) -o test test.c
- clean:
- rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions test
复制代码
make后生成isp1581.ko和test。安装模块,插入DP-1581开发板,运行test程序即可。
5 附件
附件里包括单片机固件,isp1581_linux目录包括驱动和测试程序,Linux下USB驱动开发教程。这几天正在写一个中断传输的驱动,完了再传上来。如有任何指点,感激不尽。
[ 本帖最后由 xdkui 于 2006-8-7 00:23 编辑 ] |
|