1 /* 2 * Linux driver for TerraTec DMX 6Fire USB 3 * 4 * Device communications 5 * 6 * Author: Torsten Schenk <torsten.schenk@zoho.com> 7 * Created: Jan 01, 2011 8 * Copyright: (C) Torsten Schenk 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 */ 15 16 #include "comm.h" 17 #include "chip.h" 18 #include "midi.h" 19 20 enum { 21 COMM_EP = 1, 22 COMM_FPGA_EP = 2 23 }; 24 25 static void usb6fire_comm_init_urb(struct comm_runtime *rt, struct urb *urb, 26 u8 *buffer, void *context, void(*handler)(struct urb *urb)) 27 { 28 usb_init_urb(urb); 29 urb->transfer_buffer = buffer; 30 urb->pipe = usb_sndintpipe(rt->chip->dev, COMM_EP); 31 urb->complete = handler; 32 urb->context = context; 33 urb->interval = 1; 34 urb->dev = rt->chip->dev; 35 } 36 37 static void usb6fire_comm_receiver_handler(struct urb *urb) 38 { 39 struct comm_runtime *rt = urb->context; 40 struct midi_runtime *midi_rt = rt->chip->midi; 41 42 if (!urb->status) { 43 if (rt->receiver_buffer[0] == 0x10) /* midi in event */ 44 if (midi_rt) 45 midi_rt->in_received(midi_rt, 46 rt->receiver_buffer + 2, 47 rt->receiver_buffer[1]); 48 } 49 50 if (!rt->chip->shutdown) { 51 urb->status = 0; 52 urb->actual_length = 0; 53 if (usb_submit_urb(urb, GFP_ATOMIC) < 0) 54 snd_printk(KERN_WARNING PREFIX 55 "comm data receiver aborted.\n"); 56 } 57 } 58 59 static void usb6fire_comm_init_buffer(u8 *buffer, u8 id, u8 request, 60 u8 reg, u8 vl, u8 vh) 61 { 62 buffer[0] = 0x01; 63 buffer[2] = request; 64 buffer[3] = id; 65 switch (request) { 66 case 0x02: 67 buffer[1] = 0x05; /* length (starting at buffer[2]) */ 68 buffer[4] = reg; 69 buffer[5] = vl; 70 buffer[6] = vh; 71 break; 72 73 case 0x12: 74 buffer[1] = 0x0b; /* length (starting at buffer[2]) */ 75 buffer[4] = 0x00; 76 buffer[5] = 0x18; 77 buffer[6] = 0x05; 78 buffer[7] = 0x00; 79 buffer[8] = 0x01; 80 buffer[9] = 0x00; 81 buffer[10] = 0x9e; 82 buffer[11] = reg; 83 buffer[12] = vl; 84 break; 85 86 case 0x20: 87 case 0x21: 88 case 0x22: 89 buffer[1] = 0x04; 90 buffer[4] = reg; 91 buffer[5] = vl; 92 break; 93 } 94 } 95 96 static int usb6fire_comm_send_buffer(u8 *buffer, struct usb_device *dev) 97 { 98 int ret; 99 int actual_len; 100 101 ret = usb_interrupt_msg(dev, usb_sndintpipe(dev, COMM_EP), 102 buffer, buffer[1] + 2, &actual_len, HZ); 103 if (ret < 0) 104 return ret; 105 else if (actual_len != buffer[1] + 2) 106 return -EIO; 107 return 0; 108 } 109 110 static int usb6fire_comm_write8(struct comm_runtime *rt, u8 request, 111 u8 reg, u8 value) 112 { 113 u8 buffer[13]; /* 13: maximum length of message */ 114 115 usb6fire_comm_init_buffer(buffer, 0x00, request, reg, value, 0x00); 116 return usb6fire_comm_send_buffer(buffer, rt->chip->dev); 117 } 118 119 static int usb6fire_comm_write16(struct comm_runtime *rt, u8 request, 120 u8 reg, u8 vl, u8 vh) 121 { 122 u8 buffer[13]; /* 13: maximum length of message */ 123 124 usb6fire_comm_init_buffer(buffer, 0x00, request, reg, vl, vh); 125 return usb6fire_comm_send_buffer(buffer, rt->chip->dev); 126 } 127 128 int usb6fire_comm_init(struct sfire_chip *chip) 129 { 130 struct comm_runtime *rt = kzalloc(sizeof(struct comm_runtime), 131 GFP_KERNEL); 132 struct urb *urb; 133 int ret; 134 135 if (!rt) 136 return -ENOMEM; 137 138 urb = &rt->receiver; 139 rt->serial = 1; 140 rt->chip = chip; 141 usb_init_urb(urb); 142 rt->init_urb = usb6fire_comm_init_urb; 143 rt->write8 = usb6fire_comm_write8; 144 rt->write16 = usb6fire_comm_write16; 145 146 /* submit an urb that receives communication data from device */ 147 urb->transfer_buffer = rt->receiver_buffer; 148 urb->transfer_buffer_length = COMM_RECEIVER_BUFSIZE; 149 urb->pipe = usb_rcvintpipe(chip->dev, COMM_EP); 150 urb->dev = chip->dev; 151 urb->complete = usb6fire_comm_receiver_handler; 152 urb->context = rt; 153 urb->interval = 1; 154 ret = usb_submit_urb(urb, GFP_KERNEL); 155 if (ret < 0) { 156 kfree(rt); 157 snd_printk(KERN_ERR PREFIX "cannot create comm data receiver."); 158 return ret; 159 } 160 chip->comm = rt; 161 return 0; 162 } 163 164 void usb6fire_comm_abort(struct sfire_chip *chip) 165 { 166 struct comm_runtime *rt = chip->comm; 167 168 if (rt) 169 usb_poison_urb(&rt->receiver); 170 } 171 172 void usb6fire_comm_destroy(struct sfire_chip *chip) 173 { 174 kfree(chip->comm); 175 chip->comm = NULL; 176 } 177