diff options
Diffstat (limited to 'drivers/media/rc/iguanair.c')
| -rw-r--r-- | drivers/media/rc/iguanair.c | 226 |
1 files changed, 113 insertions, 113 deletions
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index 1e4c68a5cec..627ddfd6198 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -28,6 +28,7 @@ #include <media/rc-core.h> #define DRIVER_NAME "iguanair" +#define BUF_SIZE 152 struct iguanair { struct rc_dev *rc; @@ -35,31 +36,29 @@ struct iguanair { struct device *dev; struct usb_device *udev; - int pipe_out; uint16_t version; uint8_t bufsize; + uint8_t cycle_overhead; struct mutex lock; /* receiver support */ bool receiver_on; - dma_addr_t dma_in; + dma_addr_t dma_in, dma_out; uint8_t *buf_in; - struct urb *urb_in; + struct urb *urb_in, *urb_out; struct completion completion; /* transmit support */ bool tx_overflow; uint32_t carrier; - uint8_t cycle_overhead; - uint8_t channels; - uint8_t busy4; - uint8_t busy7; + struct send_packet *packet; char name[64]; char phys[64]; }; +#define CMD_NOP 0x00 #define CMD_GET_VERSION 0x01 #define CMD_GET_BUFSIZE 0x11 #define CMD_GET_FEATURES 0x10 @@ -73,7 +72,8 @@ struct iguanair { #define DIR_IN 0xdc #define DIR_OUT 0xcd -#define MAX_PACKET_SIZE 8u +#define MAX_IN_PACKET 8u +#define MAX_OUT_PACKET (sizeof(struct send_packet) + BUF_SIZE) #define TIMEOUT 1000 #define RX_RESOLUTION 21333 @@ -191,20 +191,29 @@ static void iguanair_rx(struct urb *urb) dev_warn(ir->dev, "failed to resubmit urb: %d\n", rc); } -static int iguanair_send(struct iguanair *ir, void *data, unsigned size) +static void iguanair_irq_out(struct urb *urb) { - int rc, transferred; + struct iguanair *ir = urb->context; - INIT_COMPLETION(ir->completion); + if (urb->status) + dev_dbg(ir->dev, "Error: out urb status = %d\n", urb->status); - rc = usb_interrupt_msg(ir->udev, ir->pipe_out, data, size, - &transferred, TIMEOUT); + /* if we sent an nop packet, do not expect a response */ + if (urb->status == 0 && ir->packet->header.cmd == CMD_NOP) + complete(&ir->completion); +} + +static int iguanair_send(struct iguanair *ir, unsigned size) +{ + int rc; + + reinit_completion(&ir->completion); + + ir->urb_out->transfer_buffer_length = size; + rc = usb_submit_urb(ir->urb_out, GFP_KERNEL); if (rc) return rc; - if (transferred != size) - return -EIO; - if (wait_for_completion_timeout(&ir->completion, TIMEOUT) == 0) return -ETIMEDOUT; @@ -213,14 +222,20 @@ static int iguanair_send(struct iguanair *ir, void *data, unsigned size) static int iguanair_get_features(struct iguanair *ir) { - struct packet packet; int rc; - packet.start = 0; - packet.direction = DIR_OUT; - packet.cmd = CMD_GET_VERSION; - - rc = iguanair_send(ir, &packet, sizeof(packet)); + /* + * On cold boot, the iguanair initializes on the first packet + * received but does not process that packet. Send an empty + * packet. + */ + ir->packet->header.start = 0; + ir->packet->header.direction = DIR_OUT; + ir->packet->header.cmd = CMD_NOP; + iguanair_send(ir, sizeof(ir->packet->header)); + + ir->packet->header.cmd = CMD_GET_VERSION; + rc = iguanair_send(ir, sizeof(ir->packet->header)); if (rc) { dev_info(ir->dev, "failed to get version\n"); goto out; @@ -235,42 +250,46 @@ static int iguanair_get_features(struct iguanair *ir) ir->bufsize = 150; ir->cycle_overhead = 65; - packet.cmd = CMD_GET_BUFSIZE; + ir->packet->header.cmd = CMD_GET_BUFSIZE; - rc = iguanair_send(ir, &packet, sizeof(packet)); + rc = iguanair_send(ir, sizeof(ir->packet->header)); if (rc) { dev_info(ir->dev, "failed to get buffer size\n"); goto out; } - packet.cmd = CMD_GET_FEATURES; - - rc = iguanair_send(ir, &packet, sizeof(packet)); - if (rc) { - dev_info(ir->dev, "failed to get features\n"); - goto out; + if (ir->bufsize > BUF_SIZE) { + dev_info(ir->dev, "buffer size %u larger than expected\n", + ir->bufsize); + ir->bufsize = BUF_SIZE; } + ir->packet->header.cmd = CMD_GET_FEATURES; + + rc = iguanair_send(ir, sizeof(ir->packet->header)); + if (rc) + dev_info(ir->dev, "failed to get features\n"); out: return rc; } static int iguanair_receiver(struct iguanair *ir, bool enable) { - struct packet packet = { 0, DIR_OUT, enable ? - CMD_RECEIVER_ON : CMD_RECEIVER_OFF }; + ir->packet->header.start = 0; + ir->packet->header.direction = DIR_OUT; + ir->packet->header.cmd = enable ? CMD_RECEIVER_ON : CMD_RECEIVER_OFF; if (enable) ir_raw_event_reset(ir->rc); - return iguanair_send(ir, &packet, sizeof(packet)); + return iguanair_send(ir, sizeof(ir->packet->header)); } /* - * The iguana ir creates the carrier by busy spinning after each pulse or - * space. This is counted in CPU cycles, with the CPU running at 24MHz. It is + * The iguanair creates the carrier by busy spinning after each half period. + * This is counted in CPU cycles, with the CPU running at 24MHz. It is * broken down into 7-cycles and 4-cyles delays, with a preference for - * 4-cycle delays. + * 4-cycle delays, minus the overhead of the loop itself (cycle_overhead). */ static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier) { @@ -289,27 +308,24 @@ static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier) cycles = DIV_ROUND_CLOSEST(24000000, carrier * 2) - ir->cycle_overhead; - /* make up the the remainer of 4-cycle blocks */ - switch (cycles & 3) { - case 0: - sevens = 0; - break; - case 1: - sevens = 3; - break; - case 2: - sevens = 2; - break; - case 3: - sevens = 1; - break; - } - + /* + * Calculate minimum number of 7 cycles needed so + * we are left with a multiple of 4; so we want to have + * (sevens * 7) & 3 == cycles & 3 + */ + sevens = (4 - cycles) & 3; fours = (cycles - sevens * 7) / 4; - /* magic happens here */ - ir->busy7 = (4 - sevens) * 2; - ir->busy4 = 110 - fours; + /* + * The firmware interprets these values as a relative offset + * for a branch. Immediately following the branches, there + * 4 instructions of 7 cycles (2 bytes each) and 110 + * instructions of 4 cycles (1 byte each). A relative branch + * of 0 will execute all of them, branch further for less + * cycle burning. + */ + ir->packet->busy7 = (4 - sevens) * 2; + ir->packet->busy4 = 110 - fours; } mutex_unlock(&ir->lock); @@ -325,7 +341,7 @@ static int iguanair_set_tx_mask(struct rc_dev *dev, uint32_t mask) return 4; mutex_lock(&ir->lock); - ir->channels = mask; + ir->packet->channels = mask << 4; mutex_unlock(&ir->lock); return 0; @@ -337,68 +353,38 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count) uint8_t space; unsigned i, size, periods, bytes; int rc; - struct send_packet *packet; mutex_lock(&ir->lock); - packet = kmalloc(sizeof(*packet) + ir->bufsize, GFP_KERNEL); - if (!packet) { - rc = -ENOMEM; - goto out; - } - /* convert from us to carrier periods */ for (i = space = size = 0; i < count; i++) { periods = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000); bytes = DIV_ROUND_UP(periods, 127); if (size + bytes > ir->bufsize) { - count = i; - break; + rc = -EINVAL; + goto out; } - while (periods > 127) { - packet->payload[size++] = 127 | space; - periods -= 127; + while (periods) { + unsigned p = min(periods, 127u); + ir->packet->payload[size++] = p | space; + periods -= p; } - - packet->payload[size++] = periods | space; space ^= 0x80; } - if (count == 0) { - rc = -EINVAL; - goto out; - } - - packet->header.start = 0; - packet->header.direction = DIR_OUT; - packet->header.cmd = CMD_SEND; - packet->length = size; - packet->channels = ir->channels << 4; - packet->busy7 = ir->busy7; - packet->busy4 = ir->busy4; - - if (ir->receiver_on) { - rc = iguanair_receiver(ir, false); - if (rc) { - dev_warn(ir->dev, "disable receiver before transmit failed\n"); - goto out; - } - } + ir->packet->header.start = 0; + ir->packet->header.direction = DIR_OUT; + ir->packet->header.cmd = CMD_SEND; + ir->packet->length = size; ir->tx_overflow = false; - rc = iguanair_send(ir, packet, size + 8); + rc = iguanair_send(ir, sizeof(*ir->packet) + size); if (rc == 0 && ir->tx_overflow) rc = -EOVERFLOW; - if (ir->receiver_on) { - if (iguanair_receiver(ir, true)) - dev_warn(ir->dev, "re-enable receiver after transmit failed\n"); - } - out: - kfree(packet); mutex_unlock(&ir->lock); return rc ? rc : count; @@ -411,8 +397,6 @@ static int iguanair_open(struct rc_dev *rdev) mutex_lock(&ir->lock); - BUG_ON(ir->receiver_on); - rc = iguanair_receiver(ir, true); if (rc == 0) ir->receiver_on = true; @@ -437,13 +421,13 @@ static void iguanair_close(struct rc_dev *rdev) mutex_unlock(&ir->lock); } -static int __devinit iguanair_probe(struct usb_interface *intf, - const struct usb_device_id *id) +static int iguanair_probe(struct usb_interface *intf, + const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(intf); struct iguanair *ir; struct rc_dev *rc; - int ret, pipein; + int ret, pipein, pipeout; struct usb_host_interface *idesc; ir = kzalloc(sizeof(*ir), GFP_KERNEL); @@ -453,11 +437,14 @@ static int __devinit iguanair_probe(struct usb_interface *intf, goto out; } - ir->buf_in = usb_alloc_coherent(udev, MAX_PACKET_SIZE, GFP_KERNEL, + ir->buf_in = usb_alloc_coherent(udev, MAX_IN_PACKET, GFP_KERNEL, &ir->dma_in); + ir->packet = usb_alloc_coherent(udev, MAX_OUT_PACKET, GFP_KERNEL, + &ir->dma_out); ir->urb_in = usb_alloc_urb(0, GFP_KERNEL); + ir->urb_out = usb_alloc_urb(0, GFP_KERNEL); - if (!ir->buf_in || !ir->urb_in) { + if (!ir->buf_in || !ir->packet || !ir->urb_in || !ir->urb_out) { ret = -ENOMEM; goto out; } @@ -472,13 +459,18 @@ static int __devinit iguanair_probe(struct usb_interface *intf, ir->rc = rc; ir->dev = &intf->dev; ir->udev = udev; - ir->pipe_out = usb_sndintpipe(udev, - idesc->endpoint[1].desc.bEndpointAddress); mutex_init(&ir->lock); + init_completion(&ir->completion); + pipeout = usb_sndintpipe(udev, + idesc->endpoint[1].desc.bEndpointAddress); + usb_fill_int_urb(ir->urb_out, udev, pipeout, ir->packet, MAX_OUT_PACKET, + iguanair_irq_out, ir, 1); + ir->urb_out->transfer_dma = ir->dma_out; + ir->urb_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; pipein = usb_rcvintpipe(udev, idesc->endpoint[0].desc.bEndpointAddress); - usb_fill_int_urb(ir->urb_in, udev, pipein, ir->buf_in, MAX_PACKET_SIZE, + usb_fill_int_urb(ir->urb_in, udev, pipein, ir->buf_in, MAX_IN_PACKET, iguanair_rx, ir, 1); ir->urb_in->transfer_dma = ir->dma_in; ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; @@ -503,7 +495,7 @@ static int __devinit iguanair_probe(struct usb_interface *intf, usb_to_input_id(ir->udev, &rc->input_id); rc->dev.parent = &intf->dev; rc->driver_type = RC_DRIVER_IR_RAW; - rc->allowed_protos = RC_TYPE_ALL; + rc_set_allowed_protocols(rc, RC_BIT_ALL); rc->priv = ir; rc->open = iguanair_open; rc->close = iguanair_close; @@ -516,6 +508,7 @@ static int __devinit iguanair_probe(struct usb_interface *intf, rc->rx_resolution = RX_RESOLUTION; iguanair_set_tx_carrier(rc, 38000); + iguanair_set_tx_mask(rc, 0); ret = rc_register_device(rc); if (ret < 0) { @@ -528,26 +521,32 @@ static int __devinit iguanair_probe(struct usb_interface *intf, return 0; out2: usb_kill_urb(ir->urb_in); + usb_kill_urb(ir->urb_out); out: if (ir) { usb_free_urb(ir->urb_in); - usb_free_coherent(udev, MAX_PACKET_SIZE, ir->buf_in, - ir->dma_in); + usb_free_urb(ir->urb_out); + usb_free_coherent(udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in); + usb_free_coherent(udev, MAX_OUT_PACKET, ir->packet, + ir->dma_out); } rc_free_device(rc); kfree(ir); return ret; } -static void __devexit iguanair_disconnect(struct usb_interface *intf) +static void iguanair_disconnect(struct usb_interface *intf) { struct iguanair *ir = usb_get_intfdata(intf); rc_unregister_device(ir->rc); usb_set_intfdata(intf, NULL); usb_kill_urb(ir->urb_in); + usb_kill_urb(ir->urb_out); usb_free_urb(ir->urb_in); - usb_free_coherent(ir->udev, MAX_PACKET_SIZE, ir->buf_in, ir->dma_in); + usb_free_urb(ir->urb_out); + usb_free_coherent(ir->udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in); + usb_free_coherent(ir->udev, MAX_OUT_PACKET, ir->packet, ir->dma_out); kfree(ir); } @@ -565,6 +564,7 @@ static int iguanair_suspend(struct usb_interface *intf, pm_message_t message) } usb_kill_urb(ir->urb_in); + usb_kill_urb(ir->urb_out); mutex_unlock(&ir->lock); @@ -601,7 +601,7 @@ static const struct usb_device_id iguanair_table[] = { static struct usb_driver iguanair_driver = { .name = DRIVER_NAME, .probe = iguanair_probe, - .disconnect = __devexit_p(iguanair_disconnect), + .disconnect = iguanair_disconnect, .suspend = iguanair_suspend, .resume = iguanair_resume, .reset_resume = iguanair_resume, |
