/* * USB NUC501 driver * * Features: * * When probe, URBs in the urb pool are alloced and only will be freed when disconnect. * When open, URBs are submited to usb core and re-submit in read callback function. * When release() called, URBs should be killed all. * Use workqueue to implement interrupt(urb callback) bottom-half. * * Via function ioctl() can obtain device's hub-port location and some transport info. * * Poll supports. */ #include <linux/autoconf.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> #include <linux/smp_lock.h> #include <linux/param.h> #include <asm/system.h> #include <asm/processor.h> #include <asm/signal.h> #include <linux/list.h> #include <linux/poll.h> #include <linux/ioctl.h> #include <net/sock.h> #include <linux/netlink.h> #define NETLINK_EIGD_USB 17 #define BOOT_DEV_PRENAME "NUC501" #define RUN_DEV_PRENAME "skel" #define SKEL_IOC_MAGIC 'S' #define SKEL_IOCGDSPVER _IOR(SKEL_IOC_MAGIC,6,unsigned char) /* get dsp pcb version */ #define SKEL_IOCGSLOTNUM _IOR(SKEL_IOC_MAGIC, 0 ,unsigned char) /* get slot location */ #define SKEL_IOCGSENTCOUNT _IOR(SKEL_IOC_MAGIC, 1 ,unsigned int) /* get sent number count */ #define SKEL_IOCGFAILCOUNT _IOR(SKEL_IOC_MAGIC, 2 ,unsigned int) /* get fail number count */ #define SKEL_IOCGQUEUEMAX _IOR(SKEL_IOC_MAGIC, 3 ,unsigned int) /* get buffer queue max length */ #define SKEL_IOCGRECVCOUNT _IOR(SKEL_IOC_MAGIC, 4 ,unsigned int) /* get recv number count */ #define SKEL_IOCGDISCARDCOUNT _IOR(SKEL_IOC_MAGIC, 5 ,unsigned int) /* get discard number count */ /* Define these values to match your devices */ #define USB_SKEL_VENDOR_ID 0x0416 #define USB_SKEL_PRODUCT_ID 0x9395 #define USB_NUC501_VENDOR_ID 0x0416 #define USB_NUC501_PRODUCT_ID 0xb190 #ifndef dev_name static inline const char *dev_name(const struct device *dev) { return kobject_name(&dev->kobj); } #endif /* table of devices that work with nuc501 driver */ static struct usb_device_id nuc501_table [] = { { USB_DEVICE(USB_NUC501_VENDOR_ID,USB_NUC501_PRODUCT_ID) }, { } }; /* table of devices that work with skel driver */ static struct usb_device_id skel_table [] = { { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, skel_table); MODULE_DEVICE_TABLE (usb, nuc501_table); /* Get a minor range for your devices from the usb maintainer */ #define USB_SKEL_MINOR_BASE 192 #define USB_NUC501_MINOR_BASE 208 /* our private defines. if this grows any larger, use your own .h file */ #define MAX_TRANSFER ( PAGE_SIZE - 512 ) #define WRITES_IN_FLIGHT 20 #define MAX_LIST 500 /*buffer queue's max length*/ #define MAX_USB_PACKET_SIZE 64 /*recv urb's buffer size*/ #define DEFAULT_URBPOOL_SIZE 10 /*************** skel data structure *************/ struct data_list_node { struct list_head list; unsigned char * bulk_in_buffer; /* the buffer of received data */ int bulk_in_size; /* the size of the received data */ }; struct data_list { struct mutex listmutex; struct list_head list; int enqueue; /*list's rest,not in use length*/ int dequeue; /*list's used length*/ }; struct in_channel { spinlock_t lock; /* use in interrupt */ struct list_head list; /* in urb list */ struct workqueue_struct * workqueue; struct work_struct work; }; /* Structure to hold all of our device specific stuff */ struct usb_skel { __u8 slot_num; __u8 dsp_ver; /* DSP pcb version */ struct usb_device * udev; /* the usb device for this device */ struct usb_interface * interface; /* the interface for this device */ struct semaphore limit_sem; /* limiting the number of writes in progress */ struct urb * rcv_urb_pool[DEFAULT_URBPOOL_SIZE]; struct in_channel channel; /* read callback interrupt bottom half cache */ struct data_list rcv_data_list; /*recv data's buffer list*/ __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ int open_count; /* count the number of openers */ int present; /* this is 1 as long as the device is connected */ struct kref kref; struct mutex io_mutex; /* synchronize I/O with disconnect */ wait_queue_head_t outq; /* read wait queue */ struct fasync_struct *async_queue; /* asynchronous readers */ unsigned int discard_count; /* count the number of packets dicarded */ unsigned int fail_count; /* count the number of packets sent unsuccess */ unsigned int sent_count; /* count the number of packets sent */ unsigned int bufferqueue_maxlength; unsigned int recv_count; /* count the number of packets recieved*/ }; #define to_skel_dev(d) container_of(d, struct usb_skel, kref) /****************** nuc501 data structure ****************/ struct usb_nuc501 { struct usb_device * udev; /* the usb device for this device */ struct usb_interface * interface; /* the interface for this device */ struct semaphore limit_sem; /* limiting the number of writes in progress */ int open_count; /* count the number of openers */ 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_nuc501_dev(d) container_of(d, struct usb_nuc501, kref) /***************** global *********************/ #define DEV_NAME_MAX 10 struct usb_event { char action[DEV_NAME_MAX]; char dev_name[DEV_NAME_MAX]; int minor; }; static struct usb_driver skel_driver; static struct usb_driver nuc501_driver; static struct kmem_cache *buffer_cache; static struct kmem_cache *listnode_cache; static struct sock * usb_sock; static __u32 eigd_pid; static int skel_fasync(int fd, struct file *filp, int mode); static void send_to_user(const char * act,const char * name,int minor); /*********************** skel functions ************************/ /* 发送一段特殊的报文,获取单片机的槽位号,主机可通过IOCTL接口获取 */ static void get_slot_num(struct usb_skel * dev) { #define GET_SLOT_PACKET_SIZE 5 #define GET_SLOT_CMD 0x07 int retval; int actual_len; int count = 0; do { __u8 buffer[MAX_USB_PACKET_SIZE]; buffer[0] = GET_SLOT_CMD; retval = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), buffer, GET_SLOT_PACKET_SIZE, &actual_len, 1000); if(retval || actual_len != GET_SLOT_PACKET_SIZE) continue; mdelay(10); memset(buffer,0,sizeof(buffer)); retval = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), buffer, MAX_USB_PACKET_SIZE, &actual_len, 1000); if(retval || actual_len != GET_SLOT_PACKET_SIZE) { //printk("actual length %d/n",actual_len); continue; } if(buffer[0] == GET_SLOT_CMD) { dev->slot_num = buffer[4]; printk("Get slot num %d/n",dev->slot_num); break; } }while(count++ < 10); } static void get_dsp_pcbver(struct usb_skel * dev) { #define GET_DSPVER_PACKET_SIZE 5 #define GET_DSPVER_CMD 0x09 int retval; int actual_len; int count = 0; do { __u8 buffer[MAX_USB_PACKET_SIZE]; buffer[0] = GET_DSPVER_CMD; retval = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),